def __init__(self, sessionCacheDataDir=None, sessionCacheTimeout=None, sessionCacheAssertionClockSkewTol=1.0): '''Initialise settings for connection to an Attribute Authority @param sessionCacheDataDir: directory for permanent storage of sessions. Sessions are used as a means of optimisation caching Attribute Query results to reduce the number of Attribute Authority web service calls. If set to None, sessions are cached in memory only. @type sessionCacheDataDir: None type / basestring @param sessionCacheTimeout: time in seconds for individual caches' lifetimes. Set to None to set no expiry. @type sessionCacheTimeout: float/int/long/string or None type ''' self.sessionCacheDataDir = sessionCacheDataDir self.sessionCacheTimeout = sessionCacheTimeout self.__sessionCacheAssertionClockSkewTol = \ sessionCacheAssertionClockSkewTol self.__subjectAttributeId = None self.__mappingFilePath = None # Force mapping dict to have string type keys and items _typeCheckers = (lambda val: isinstance(val, basestring), ) * 2 self.__attributeId2AttributeAuthorityMap = VettedDict(*_typeCheckers) self.__attribute_query = AttributeQueryFactory.create() self.__attribute_query_binding = AttributeQuerySslSOAPBinding() self.__cacheSessions = True
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)
def __init__(self, sessionCacheDataDir=None, sessionCacheTimeout=None, sessionCacheAssertionClockSkewTol=1.0): '''Initialise settings for connection to an Attribute Authority @param sessionCacheDataDir: directory for permanent storage of sessions. Sessions are used as a means of optimisation caching Attribute Query results to reduce the number of Attribute Authority web service calls. If set to None, sessions are cached in memory only. @type sessionCacheDataDir: None type / basestring @param sessionCacheTimeout: time in seconds for individual caches' lifetimes. Set to None to set no expiry. @type sessionCacheTimeout: float/int/long/string or None type ''' self.sessionCacheDataDir = sessionCacheDataDir self.sessionCacheTimeout = sessionCacheTimeout self.__sessionCacheAssertionClockSkewTol = \ sessionCacheAssertionClockSkewTol self.__subjectAttributeId = None self.__mappingFilePath = None # Force mapping dict to have string type keys and items _typeCheckers = (lambda val: isinstance(val, basestring),)*2 self.__attributeId2AttributeAuthorityMap = VettedDict(*_typeCheckers) self.__attribute_query = AttributeQueryFactory.create() self.__attribute_query_binding = AttributeQuerySslSOAPBinding() self.__cacheSessions = True
def test02_from_kw(self): attribute_query = AttributeQueryFactory.from_kw( prefix='attributeQuery.', **self.config) self.assertEqual(attribute_query.subject.nameID.format, self.config['attributeQuery.subject.nameID.format'], 'Parameter is %r, expected %r' % ( attribute_query.subject.nameID.format, self.config['attributeQuery.subject.nameID.format'])) self.assertEqual(attribute_query.issuer.value, self.config['attributeQuery.issuer.value'], 'Parameter is %r, expected %r' % ( attribute_query.issuer.value, self.config['attributeQuery.issuer.value'])) self.assertEqual(len(attribute_query.attributes), 2, 'expecting 2 SAML attributes parsed') attr = None for attr in attribute_query.attributes: if attr.friendlyName == 'FirstName': break self.assertNotEqual(attr, None, 'Missing expected friendlyName attribute') self.assertIn(attr.nameFormat, self.config['attributeQuery.attributes.0'], 'Parameter is %r, not found in %r' % ( attr.nameFormat, self.config['attributeQuery.attributes.0'])) self.assertIn(attr.name, self.config['attributeQuery.attributes.0'], 'Parameter is %r, not found in %r' % ( attr.name, self.config['attributeQuery.attributes.0']))
def attributeQuery(self, context, attributeDesignator): """Query this PIP for the given request context attribute specified by the attribute designator. Nb. this implementation is only intended to accept queries for a given *subject* in the request @param context: the request context @type context: ndg.xacml.core.context.request.Request @param designator: @type designator: ndg.xacml.core.attributedesignator.SubjectAttributeDesignator @rtype: ndg.xacml.utils.TypedList(<attributeDesignator.dataType>) / None @return: attribute values found for query subject or None if none could be found @raise PIPConfigException: if attribute ID -> Attribute Authority mapping is empty """ # Check the attribute designator type - this implementation takes # queries for request context subjects only if not isinstance(attributeDesignator, SubjectAttributeDesignator): log.debug('This PIP query interface can only accept subject ' 'attribute designator related queries') return None attributeFormat = attributeDesignator.dataType attributeId = attributeDesignator.attributeId exptd_attribute_id = self.attribute_query.subject.nameID.format if not isinstance(context, XacmlRequestCtx): raise TypeError('Expecting %r type for context input; got %r' % (XacmlRequestCtx, type(context))) # Look up mapping from request attribute ID to Attribute Authority to # query if len(self.__attributeId2AttributeAuthorityMap) == 0: raise PIPConfigException('No entries found in attribute ID to ' 'Attribute Authority mapping') attributeAuthorityURI = self.__attributeId2AttributeAuthorityMap.get( attributeId, None) if attributeAuthorityURI is None: log.debug( "No matching attribute authority endpoint found in " "mapping file %r for input attribute ID %r", self.mappingFilePath, attributeId) return None # Get subject from the request context subject = None subjectId = None for subject in context.subjects: for attribute in subject.attributes: if attribute.attributeId == exptd_attribute_id: if len(attribute.attributeValues) != 1: raise PIPRequestCtxException("Expecting a single " "attribute value " "for query subject ID") subjectId = attribute.attributeValues[0].value break if subjectId is None: raise PIPRequestCtxException('No subject found of type %r in ' 'request context' % exptd_attribute_id) elif not subjectId: # Empty string return None else: # Keep a reference to the matching Subject instance xacmlCtxSubject = subject # Check for cached attributes for this subject (i.e. user) # If none found send a query to the attribute authority assertions = None attributeIdFoundInCache = False if self.cacheSessions: attributeIdFoundInCache = False sessionCache = SessionCache(subjectId, data_dir=self.sessionCacheDataDir, timeout=self.sessionCacheTimeout, assertionClockSkewTolerance=\ self.sessionCacheAssertionClockSkewTol) assertions = sessionCache.retrieve(attributeAuthorityURI) if assertions is not None: # Check for attributes matching the requested ID for assertion in assertions: for statement in assertion.attributeStatements: for attribute in statement.attributes: if attribute.name == attributeId: attributeIdFoundInCache = True break if not attributeIdFoundInCache: # No cached assertions are available for this Attribute Authority, # for the required attribute ID - make a fresh call to the # Attribute Authority # Initialise the attribute to be queried for and add it to the # SAML query samlAttribute = SamlAttribute() samlAttribute.name = attributeId samlAttribute.nameFormat = attributeFormat # Copy attributes for this query from constants set at # initialisation query = AttributeQueryFactory.create() query.subject.nameID.value = subjectId query.subject.nameID.format = exptd_attribute_id query.issuer.value = self.attribute_query.issuer.value query.issuer.format = self.attribute_query.issuer.format query.attributes.append(samlAttribute) # Dispatch query try: response = self.attribute_query_binding.send( query, uri=attributeAuthorityURI) log.debug('Retrieved response from attribute service %r', attributeAuthorityURI) except Exception: log.exception( 'Error querying Attribute service %r with ' 'subject %r', attributeAuthorityURI, subjectId) raise if assertions is None: assertions = SamlTypedList(SamlAssertion) assertions.extend(response.assertions) if self.cacheSessions: sessionCache.add(assertions, attributeAuthorityURI) # Unpack SAML assertion attribute corresponding to the name # format specified and copy into XACML attributes xacmlAttribute = XacmlAttribute() xacmlAttribute.attributeId = attributeId xacmlAttribute.dataType = attributeFormat # Create XACML class from SAML type identifier factory = self.__class__.XACML_ATTR_VAL_CLASS_FACTORY xacmlAttrValClass = factory(attributeFormat) for assertion in assertions: for statement in assertion.attributeStatements: for attribute in statement.attributes: if attribute.nameFormat == attributeFormat: # Convert SAML Attribute values to XACML equivalent # types for samlAttrVal in attribute.attributeValues: # Instantiate and initial new XACML value xacmlAttrVal = xacmlAttrValClass( value=samlAttrVal.value) xacmlAttribute.attributeValues.append(xacmlAttrVal) # Update the XACML request context subject with the new attributes matchFound = False for attr in xacmlCtxSubject.attributes: matchFound = attr.attributeId == attributeId if matchFound: # Weed out duplicates newAttrVals = [ attrVal for attrVal in xacmlAttribute.attributeValues if attrVal not in attr.attributeValues ] attr.attributeValues.extend(newAttrVals) break if not matchFound: xacmlCtxSubject.attributes.append(xacmlAttribute) # Return the attributes to the caller to comply with the interface return xacmlAttribute.attributeValues
def attributeQuery(self, context, attributeDesignator): """Query this PIP for the given request context attribute specified by the attribute designator. Nb. this implementation is only intended to accept queries for a given *subject* in the request @param context: the request context @type context: ndg.xacml.core.context.request.Request @param designator: @type designator: ndg.xacml.core.attributedesignator.SubjectAttributeDesignator @rtype: ndg.xacml.utils.TypedList(<attributeDesignator.dataType>) / None @return: attribute values found for query subject or None if none could be found @raise PIPConfigException: if attribute ID -> Attribute Authority mapping is empty """ # Check the attribute designator type - this implementation takes # queries for request context subjects only if not isinstance(attributeDesignator, SubjectAttributeDesignator): log.debug('This PIP query interface can only accept subject ' 'attribute designator related queries') return None attributeFormat = attributeDesignator.dataType attributeId = attributeDesignator.attributeId exptd_attribute_id = self.attribute_query.subject.nameID.format if not isinstance(context, XacmlRequestCtx): raise TypeError('Expecting %r type for context input; got %r' % (XacmlRequestCtx, type(context))) # Look up mapping from request attribute ID to Attribute Authority to # query if len(self.__attributeId2AttributeAuthorityMap) == 0: raise PIPConfigException('No entries found in attribute ID to ' 'Attribute Authority mapping') attributeAuthorityURI = self.__attributeId2AttributeAuthorityMap.get( attributeId, None) if attributeAuthorityURI is None: log.debug("No matching attribute authority endpoint found in " "mapping file %r for input attribute ID %r", self.mappingFilePath, attributeId) return None # Get subject from the request context subject = None subjectId = None for subject in context.subjects: for attribute in subject.attributes: if attribute.attributeId == exptd_attribute_id: if len(attribute.attributeValues) != 1: raise PIPRequestCtxException("Expecting a single " "attribute value " "for query subject ID") subjectId = attribute.attributeValues[0].value break if subjectId is None: raise PIPRequestCtxException('No subject found of type %r in ' 'request context' % exptd_attribute_id) elif not subjectId: # Empty string return None else: # Keep a reference to the matching Subject instance xacmlCtxSubject = subject # Check for cached attributes for this subject (i.e. user) # If none found send a query to the attribute authority assertions = None attributeIdFoundInCache = False if self.cacheSessions: attributeIdFoundInCache = False sessionCache = SessionCache(subjectId, data_dir=self.sessionCacheDataDir, timeout=self.sessionCacheTimeout, assertionClockSkewTolerance=\ self.sessionCacheAssertionClockSkewTol) assertions = sessionCache.retrieve(attributeAuthorityURI) if assertions is not None: # Check for attributes matching the requested ID for assertion in assertions: for statement in assertion.attributeStatements: for attribute in statement.attributes: if attribute.name == attributeId: attributeIdFoundInCache = True break if not attributeIdFoundInCache: # No cached assertions are available for this Attribute Authority, # for the required attribute ID - make a fresh call to the # Attribute Authority # Initialise the attribute to be queried for and add it to the # SAML query samlAttribute = SamlAttribute() samlAttribute.name = attributeId samlAttribute.nameFormat = attributeFormat # Copy attributes for this query from constants set at # initialisation query = AttributeQueryFactory.create() query.subject.nameID.value = subjectId query.subject.nameID.format = exptd_attribute_id query.issuer.value = self.attribute_query.issuer.value query.issuer.format = self.attribute_query.issuer.format query.attributes.append(samlAttribute) # Dispatch query try: response = self.attribute_query_binding.send(query, uri=attributeAuthorityURI) log.debug('Retrieved response from attribute service %r', attributeAuthorityURI) except Exception: log.exception('Error querying Attribute service %r with ' 'subject %r', attributeAuthorityURI, subjectId) raise if assertions is None: assertions = SamlTypedList(SamlAssertion) assertions.extend(response.assertions) if self.cacheSessions: sessionCache.add(assertions, attributeAuthorityURI) # Unpack SAML assertion attribute corresponding to the name # format specified and copy into XACML attributes xacmlAttribute = XacmlAttribute() xacmlAttribute.attributeId = attributeId xacmlAttribute.dataType = attributeFormat # Create XACML class from SAML type identifier factory = self.__class__.XACML_ATTR_VAL_CLASS_FACTORY xacmlAttrValClass = factory(attributeFormat) for assertion in assertions: for statement in assertion.attributeStatements: for attribute in statement.attributes: if attribute.nameFormat == attributeFormat: # Convert SAML Attribute values to XACML equivalent # types for samlAttrVal in attribute.attributeValues: # Instantiate and initial new XACML value xacmlAttrVal = xacmlAttrValClass( value=samlAttrVal.value) xacmlAttribute.attributeValues.append(xacmlAttrVal) # Update the XACML request context subject with the new attributes matchFound = False for attr in xacmlCtxSubject.attributes: matchFound = attr.attributeId == attributeId if matchFound: # Weed out duplicates newAttrVals = [attrVal for attrVal in xacmlAttribute.attributeValues if attrVal not in attr.attributeValues] attr.attributeValues.extend(newAttrVals) break if not matchFound: xacmlCtxSubject.attributes.append(xacmlAttribute) # Return the attributes to the caller to comply with the interface return xacmlAttribute.attributeValues
def test01_create(self): attribute_query = AttributeQueryFactory.create() self.assertIsNotNone(attribute_query.subject, 'query subject is none') self.assertIsNotNone(attribute_query.issuer, 'query issuer is none')