Changeset 6578 for TI12-security/trunk/NDGSecurity
- Timestamp:
- 16/02/10 16:11:08 (11 years ago)
- Location:
- TI12-security/trunk/NDGSecurity/python
- Files:
-
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/binding/soap/__init__.py
r6572 r6578 188 188 189 189 samlElem = self.serialise(samlObj) 190 190 191 191 # Attach query to SOAP body 192 192 request.envelope.body.elem.append(samlElem) -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/binding/soap/attributequery.py
r6572 r6578 48 48 SERIALISE_KW = 'serialise' 49 49 DESERIALISE_KW = 'deserialise' 50 QUERY_TYPE = AttributeQuery 50 51 51 52 def __init__(self, **kw): 52 53 '''Create SOAP Client for SAML Attribute Query''' 53 self.__queryAttributes = TypedList(Attribute)54 54 55 55 # Default to ElementTree based serialisation/deserialisation … … 65 65 66 66 super(AttributeQuerySOAPBinding, self).__init__(**kw) 67 self.__queryAttributes = TypedList(Attribute) 67 68 68 69 def __setattr__(self, name, value): … … 107 108 doc="List of attributes to query from the " 108 109 "Attribute Authority") 109 110 def _createQuery(self):111 """ Create a SAML attribute query"""112 attributeQuery = super(AttributeQuerySOAPBinding, self)._createQuery(113 AttributeQuery)114 # Add list of attributes to query115 for attribute in self.queryAttributes:116 attributeQuery.attributes.append(attribute)117 118 return attributeQuery110 # 111 # def _createQuery(self): 112 # """ Create a SAML attribute query""" 113 # attributeQuery = super(AttributeQuerySOAPBinding, self)._createQuery( 114 # AttributeQuery) 115 # # Add list of attributes to query 116 # for attribute in self.queryAttributes: 117 # attributeQuery.attributes.append(attribute) 118 # 119 # return attributeQuery 119 120 120 121 -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/binding/soap/authzdecisionquery.py
r6572 r6578 17 17 from saml.saml2.core import AuthzDecisionQuery 18 18 19 from ndg.security.common.sam _utils.binding.soap.subjectquery import (19 from ndg.security.common.saml_utils.binding.soap.subjectquery import ( 20 20 SubjectQuerySOAPBinding, 21 21 SubjectQueryResponseError) … … 40 40 Nb. Assumes X.509 subject type for query issuer 41 41 """ 42 __slots__ = () 42 SERIALISE_KW = 'serialise' 43 DESERIALISE_KW = 'deserialise' 44 QUERY_TYPE = AuthzDecisionQuery 45 __slots__ = ('__resourceURI', '__action', '__actionNs') 43 46 44 47 def __init__(self, **kw): 45 48 '''Create SOAP Client for SAML Authorization Decision Query''' 49 cls = AuthzDecisionQuerySOAPBinding 50 51 # Default to ElementTree based serialisation/deserialisation 52 if cls.SERIALISE_KW not in kw: 53 from saml.xml.etree import AuthzDecisionQueryElementTree 54 kw[cls.SERIALISE_KW] = AuthzDecisionQueryElementTree.toXML 55 56 if cls.DESERIALISE_KW not in kw: 57 from saml.xml.etree import ResponseElementTree 58 kw[cls.DESERIALISE_KW] = ResponseElementTree.fromXML 59 46 60 super(AuthzDecisionQuerySOAPBinding, self).__init__(**kw) 47 61 62 def _getResourceURI(self): 63 return self.query.resource 64 65 def _setResourceURI(self, value): 66 self.query.resource = value 67 68 resourceURI = property(_getResourceURI, _setResourceURI, 69 doc="Resource URI to query for access") 48 70 49 def _createQuery(self): 50 """Specialisation to force creation of AuthzDecisionQuery type instead 51 of SubjectQuery 52 """ 53 return super(AuthzDecisionQuerySOAPBinding, self)._createQuery( 54 AuthzDecisionQuery) 71 @property 72 def actions(self): 73 return self.query.actions 55 74 56 75 -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/binding/soap/subjectquery.py
r6572 r6578 56 56 """ 57 57 SUBJECT_ID_OPTNAME = 'subjectID' 58 SUBJECT_ID_FORMAT_OPTNAME = 'subjectIdFormat' 58 59 ISSUER_NAME_OPTNAME = 'issuerName' 59 60 ISSUER_FORMAT_OPTNAME = 'issuerFormat' 60 SUBJECT_ID_FORMAT_OPTNAME = 'subjectIdFormat'61 61 CLOCK_SKEW_OPTNAME = 'clockSkewTolerance' 62 62 VERIFY_TIME_CONDITIONS_OPTNAME = 'verifyTimeConditions' … … 72 72 73 73 __PRIVATE_ATTR_PREFIX = "__" 74 __slots__ = tuple([__PRIVATE_ATTR_PREFIX + i for i in CONFIG_FILE_OPTNAMES]) 74 __slots__ = tuple([__PRIVATE_ATTR_PREFIX + i 75 for i in CONFIG_FILE_OPTNAMES + ('query', )]) 75 76 del i 76 77 78 QUERY_TYPE = SubjectQuery 79 77 80 def __init__(self, **kw): 78 '''Create SOAP Client for SAML Attribute Query''' 79 self.__issuerName = None 80 self.__issuerFormat = Issuer.X509_SUBJECT 81 self.__subjectID = None 82 self.__subjectIdFormat = NameID.UNSPECIFIED 81 '''Create SOAP Client for SAML Subject Query''' 83 82 self.__clockSkewTolerance = timedelta(seconds=0.) 84 83 self.__verifyTimeConditions = True 85 84 85 self._initQuery() 86 86 87 super(SubjectQuerySOAPBinding, self).__init__(**kw) 87 88 88 def _getNameIdFormat(self): 89 return self.__subjectIdFormat 90 91 def _setNameIdFormat(self, value): 92 self.__subjectIdFormat = value 93 94 subjectIdFormat = property(_getNameIdFormat, _setNameIdFormat, 95 doc="Subject Name ID format") 89 def _initQuery(self): 90 """Initialise query settings""" 91 self.__query = self.__class__.QUERY_TYPE() 92 self.__query.version = SAMLVersion(SAMLVersion.VERSION_20) 93 self.__query.id = str(uuid4()) 94 95 # These properties access the __query instance 96 self.issuerFormat = Issuer.X509_SUBJECT 97 self.subjectIdFormat = NameID.UNSPECIFIED 98 99 def _getQuery(self): 100 return self.__query 101 102 def _setQuery(self, value): 103 if not isinstance(value, self.__class__.QUERY_TYPE): 104 raise TypeError('Expecting %r query type got %r instead' % 105 (self.__class__, type(value))) 106 self.__query = value 107 108 query = property(_getQuery, _setQuery, 109 doc="SAML Subject Query or derived query type") 110 111 def _getSubjectID(self): 112 if self.__query.subject is None or self.__query.subject.nameID is None: 113 return None 114 else: 115 return self.__query.subject.nameID.value 116 117 def _setSubjectID(self, value): 118 if self.__query.subject is None: 119 self.__query.subject = Subject() 120 121 if self.__query.subject.nameID is None: 122 self.__query.subject.nameID = NameID() 123 124 self.__query.subject.nameID.value = value 125 126 subjectID = property(_getSubjectID, _setSubjectID, 127 doc="ID to be sent as query subject") 128 129 def _getSubjectIdFormat(self): 130 if self.__query.subject is None or self.__query.subject.nameID is None: 131 return None 132 else: 133 return self.__query.subject.nameID.format 134 135 def _setSubjectIdFormat(self, value): 136 if self.__query.subject is None: 137 self.__query.subject = Subject() 138 139 if self.__query.subject.nameID is None: 140 self.__query.subject.nameID = NameID() 141 142 self.__query.subject.nameID.format = value 143 144 subjectIdFormat = property(_getSubjectIdFormat, _setSubjectIdFormat, 145 doc="Subject Name ID format") 96 146 97 147 def _getIssuerFormat(self): 98 return self.__issuerFormat 148 if self.__query.issuer is None: 149 return None 150 else: 151 return self.__query.issuer.value 99 152 100 153 def _setIssuerFormat(self, value): 101 if not isinstance(value, basestring):102 raise TypeError('Expecting string type for "issuerFormat"; got %r '103 'instead' % type(value))104 self.__ issuerFormat = value154 if self.__query.issuer is None: 155 self.__query.issuer = Issuer() 156 157 self.__query.issuer.format = value 105 158 106 159 issuerFormat = property(_getIssuerFormat, _setIssuerFormat, 107 160 doc="Issuer format") 161 162 def _getIssuerName(self): 163 if self.__query.issuer is None: 164 return None 165 else: 166 return self.__query.issuer.value 167 168 def _setIssuerName(self, value): 169 if self.__query.issuer is None: 170 self.__query.issuer = Issuer() 171 172 self.__query.issuer.value = value 173 174 issuerName = property(_getIssuerName, _setIssuerName, 175 doc="Distinguished Name of issuer of SAML Attribute " 176 "Query to Attribute Authority") 108 177 109 178 def _getVerifyTimeConditions(self): … … 125 194 doc='Set to True to verify any time ' 126 195 'Conditions set in the returned ' 127 'response assertions') 128 129 def _getSubjectID(self): 130 return self.__subjectID 131 132 def _setSubjectID(self, value): 133 if not isinstance(value, basestring): 134 raise TypeError('Expecting string type for "subjectID"; got %r ' 135 'instead' % type(value)) 136 self.__subjectID = value 137 138 subjectID = property(_getSubjectID, _setSubjectID, 139 doc="ID to be sent as query subject") 140 141 def _getIssuerName(self): 142 return self.__issuerName 143 144 def _setIssuerName(self, value): 145 if not isinstance(value, basestring): 146 raise TypeError('Expecting string type for "issuerName"; ' 147 'got %r instead' % type(value)) 148 149 self.__issuerName = value 150 151 issuerName = property(_getIssuerName, _setIssuerName, 152 doc="Distinguished Name of issuer of SAML Attribute " 153 "Query to Attribute Authority") 196 'response assertions') 154 197 155 198 def _getClockSkewTolerance(self): … … 172 215 "assertion condition notBefore and " 173 216 "notOnOrAfter times to allow for clock " 174 "skew") 175 176 def _createQuery(self, queryClass=SubjectQuery): 177 """ Create a SAML SubjectQuery derived type instance 178 @param queryClass: query type to create - must be 179 saml.saml2.core.SubjectQuery type 180 @type queryClass: type 181 @return: query instance 182 @rtype: saml.saml2.core.SubjectQuery 183 """ 184 if not issubclass(queryClass, SubjectQuery): 185 raise TypeError('Query class %r is not a SubjectQuery derived type' 186 % queryClass) 187 188 query = queryClass() 189 query.version = SAMLVersion(SAMLVersion.VERSION_20) 190 query.id = str(uuid4()) 191 query.issueInstant = datetime.utcnow() 192 217 "skew") 218 219 def _validateQueryParameters(self): 220 """Perform sanity check immediately before creating the query and 221 sending it""" 193 222 if self.issuerName is None: 194 raise AttributeError('No issuer DN has been set for SAML Query') 195 196 query.issuer = Issuer() 197 query.issuer.format = self.issuerFormat 198 query.issuer.value = self.issuerName 199 200 query.subject = Subject() 201 query.subject.nameID = NameID() 202 query.subject.nameID.format = self.subjectIdFormat 203 query.subject.nameID.value = self.subjectID 204 205 return query 223 raise AttributeError('No issuer name has been set for SAML Query') 224 225 if self.issuerFormat is None: 226 raise AttributeError('No issuer format has been set for SAML Query') 227 228 if self.subjectID is None: 229 raise AttributeError('No subject has been set for SAML Query') 230 231 if self.subjectIdFormat is None: 232 raise AttributeError('No subject format has been set for SAML ' 233 'Query') 234 235 def _initSend(self): 236 """Perform any final initialisation prior to sending the query - derived 237 classes may overload to specify as required""" 238 self.__query.issueInstant = datetime.utcnow() 206 239 207 240 def send(self, **kw): … … 214 247 defaults to ndg.security.common.soap.client.UrlLib2SOAPRequest 215 248 ''' 216 query = self._createQuery() 217 218 response = super(SubjectQuerySOAPBinding, self).send(query, **kw) 249 self._validateQueryParameters() 250 self._initSend() 251 252 response = super(SubjectQuerySOAPBinding, self).send(self.query, **kw) 219 253 220 254 # Perform validation -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/esg/xml/etree.py
r6069 r6578 16 16 17 17 from saml.xml import XMLTypeParseError, UnknownAttrProfile 18 from saml.xml.etree import AttributeValueElementTreeBase, QName 18 from saml.xml.etree import (AttributeValueElementTreeBase, ResponseElementTree, 19 QName) 19 20 20 21 from ndg.security.common.saml_utils.esg import XSGroupRoleAttributeValue … … 138 139 return None 139 140 141 142 class EsgResponseElementTree(ResponseElementTree): 143 """Extend ResponseElementTree type for Attribute Query Response to include 144 ESG custom Group/Role Attribute support""" 145 146 @classmethod 147 def toXML(cls, response, **kw): 148 # Add mapping for ESG Group/Role Attribute Value to enable ElementTree 149 # Attribute Value factory to render the XML output 150 toXMLTypeMap = kw.get('customToXMLTypeMap', {}) 151 toXMLTypeMap[XSGroupRoleAttributeValue 152 ] = XSGroupRoleAttributeValueElementTree 153 154 kw['customToXMLTypeMap'] = toXMLTypeMap 155 156 # Convert to ElementTree representation to enable attachment to SOAP 157 # response body 158 return ResponseElementTree.toXML(response, **kw) 159 160 @classmethod 161 def fromXML(cls, elem, **kw): 162 toSAMLTypeMap = kw.get('customToSAMLTypeMap', []) 163 toSAMLTypeMap.append( 164 XSGroupRoleAttributeValueElementTree.factoryMatchFunc) 165 kw['customToSAMLTypeMap'] = toSAMLTypeMap 166 167 return ResponseElementTree.fromXML(elem, **kw) -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/soap/client.py
r6063 r6578 137 137 def __init__(self): 138 138 self.__fileobject = None 139 140 def _getFileobject(self): 139 140 @property 141 def fileobject(self): 142 "urllib2 file object returned from request" 141 143 return self.__fileobject 142 143 fileobject = property(fget=_getFileobject,144 doc="urllib2 file object returned from request")145 144 146 145 147 146 class UrlLib2SOAPClient(SOAPClientBase): 148 147 """urllib2 based SOAP Client""" 148 DEFAULT_HTTP_HEADER = {'Content-type': 'text/xml'} 149 149 150 150 def __init__(self): … … 154 154 self.__openerDirector.add_handler(urllib2.HTTPHandler()) 155 155 self.__timeout = None 156 self.__httpHeader = UrlLib2SOAPClient.DEFAULT_HTTP_HEADER.copy() 157 158 def _getHttpHeader(self): 159 return self.__httpHeader 160 161 def _setHttpHeader(self, value): 162 if not isinstance(value, dict): 163 raise TypeError('Expecting dict type for "httpHeader" attribute; ' 164 'got %r instead' % type(value)) 165 self.__httpHeader = value 166 167 httpHeader = property(_getHttpHeader, _setHttpHeader, 168 doc="Set HTTP header fields in this dict object") 156 169 157 170 def _getTimeout(self): … … 203 216 204 217 soapRequestStr = soapRequest.envelope.serialize() 205 206 if log. level<= logging.DEBUG:218 219 if log.getEffectiveLevel() <= logging.DEBUG: 207 220 from ndg.security.common.utils.etree import prettyPrint 208 221 log.debug("SOAP Request:") … … 211 224 212 225 soapResponse = UrlLib2SOAPResponse() 213 response = self.openerDirector.open(soapRequest.url, 226 urllib2Request = urllib2.Request(soapRequest.url) 227 for i in self.httpHeader.items(): 228 urllib2Request.add_header(*i) 229 230 response = self.openerDirector.open(urllib2Request, 214 231 soapRequestStr, 215 232 *arg) 216 233 if response.code != httplib.OK: 217 output = response.read()218 234 excep = HTTPException("Response for request to [%s] is: %d %s" % 219 235 (soapRequest.url, -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/soap/etree.py
r5601 r6578 148 148 149 149 self.qname = QName(SOAPEnvelopeBase.DEFAULT_ELEMENT_NS, 150 151 150 tag=SOAPEnvelopeBase.DEFAULT_ELEMENT_LOCAL_NAME, 151 prefix=SOAPEnvelopeBase.DEFAULT_ELEMENT_NS_PREFIX) 152 152 self.__header = SOAPHeader() 153 153 self.__body = SOAPBody() -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/saml/__init__.py
r6573 r6578 9 9 __contact__ = "Philip.Kershaw@stfc.ac.uk" 10 10 __revision__ = "$Id: $" 11 __license__ = "BSD - see LICENSE file in top-levle directory" 11 __license__ = "BSD - see LICENSE file in top-level directory" 12 import logging 13 log = logging.getLogger(__name__) 14 import traceback 15 from cStringIO import StringIO 16 from uuid import uuid4 17 from datetime import datetime 18 from xml.etree import ElementTree 19 20 from saml.saml2.core import Response, AttributeQuery, Status, StatusCode 21 from saml.xml import UnknownAttrProfile 22 from saml.xml.etree import AttributeQueryElementTree, ResponseElementTree 23 24 from ndg.security.common.saml_utils.esg import XSGroupRoleAttributeValue 25 from ndg.security.common.saml_utils.esg.xml.etree import ( 26 XSGroupRoleAttributeValueElementTree) 27 from ndg.security.common.soap.etree import SOAPEnvelope 28 from ndg.security.common.utils.factory import importModuleObject 29 from ndg.security.server.wsgi import NDGSecurityPathFilter 30 from ndg.security.server.wsgi.soap import SOAPMiddleware 31 32 33 class SOAPQueryInterfaceMiddlewareError(Exception): 34 """Base class for WSGI SAML 2.0 SOAP Query Interface Errors""" 35 36 37 class SOAPQueryInterfaceMiddlewareConfigError(Exception): 38 """WSGI SAML 2.0 SOAP Query Interface Configuration problem""" 39 40 41 class SOAPQueryInterfaceMiddleware(SOAPMiddleware, NDGSecurityPathFilter): 42 """Implementation of SAML 2.0 SOAP Binding for Query/Request Binding 43 44 @type PATH_OPTNAME: basestring 45 @cvar PATH_OPTNAME: name of app_conf option for specifying a path or paths 46 that this middleware will intercept and process 47 @type QUERY_INTERFACE_KEYNAME_OPTNAME: basestring 48 @cvar QUERY_INTERFACE_KEYNAME_OPTNAME: app_conf option name for key name 49 used to reference the SAML query interface in environ 50 @type DEFAULT_QUERY_INTERFACE_KEYNAME: basestring 51 @param DEFAULT_QUERY_INTERFACE_KEYNAME: default key name for referencing 52 SAML query interface in environ 53 """ 54 log = logging.getLogger('SOAPQueryInterfaceMiddleware') 55 PATH_OPTNAME = "pathMatchList" 56 QUERY_INTERFACE_KEYNAME_OPTNAME = "queryInterfaceKeyName" 57 DEFAULT_QUERY_INTERFACE_KEYNAME = ("ndg.security.server.wsgi.saml." 58 "SOAPQueryInterfaceMiddleware.queryInterface") 59 60 REQUEST_ENVELOPE_CLASS_OPTNAME = 'requestEnvelopeClass' 61 RESPONSE_ENVELOPE_CLASS_OPTNAME = 'responseEnvelopeClass' 62 SERIALISE_OPTNAME = 'serialise' 63 DESERIALISE_OPTNAME = 'deserialise' 64 65 CONFIG_FILE_OPTNAMES = ( 66 PATH_OPTNAME, 67 QUERY_INTERFACE_KEYNAME_OPTNAME, 68 DEFAULT_QUERY_INTERFACE_KEYNAME, 69 REQUEST_ENVELOPE_CLASS_OPTNAME, 70 RESPONSE_ENVELOPE_CLASS_OPTNAME, 71 SERIALISE_OPTNAME, 72 DESERIALISE_OPTNAME 73 ) 74 75 def __init__(self, app): 76 '''@type app: callable following WSGI interface 77 @param app: next middleware application in the chain 78 ''' 79 NDGSecurityPathFilter.__init__(self, app, None) 80 81 self._app = app 82 83 # Set defaults 84 cls = SOAPQueryInterfaceMiddleware 85 self.__queryInterfaceKeyName = cls.DEFAULT_QUERY_INTERFACE_KEYNAME 86 self.pathMatchList = ['/'] 87 self.__requestEnvelopeClass = None 88 self.__responseEnvelopeClass = None 89 self.__serialise = None 90 self.__deserialise = None 91 92 def initialise(self, global_conf, prefix='', **app_conf): 93 ''' 94 @type global_conf: dict 95 @param global_conf: PasteDeploy global configuration dictionary 96 @type prefix: basestring 97 @param prefix: prefix for configuration items 98 @type app_conf: dict 99 @param app_conf: PasteDeploy application specific configuration 100 dictionary 101 ''' 102 cls = SOAPQueryInterfaceMiddleware 103 104 # Override where set in config 105 for name in SOAPQueryInterfaceMiddleware.CONFIG_FILE_OPTNAMES: 106 val = app_conf.get(prefix + name) 107 if val is not None: 108 setattr(self, name, val) 109 110 if self.serialise is None: 111 raise AttributeError('No "serialise" method set to serialise the ' 112 'SAML response from this middleware.') 113 114 if self.deserialise is None: 115 raise AttributeError('No "deserialise" method set to parse the ' 116 'SAML request to this middleware.') 117 118 def _getSerialise(self): 119 return self.__serialise 120 121 def _setSerialise(self, value): 122 if isinstance(value, basestring): 123 self.__serialise = importModuleObject(value) 124 125 elif callable(value): 126 self.__serialise = value 127 else: 128 raise TypeError('Expecting callable for "serialise"; got %r' % 129 value) 130 131 serialise = property(_getSerialise, _setSerialise, 132 doc="callable to serialise request into XML type") 133 134 def _getDeserialise(self): 135 return self.__deserialise 136 137 def _setDeserialise(self, value): 138 if isinstance(value, basestring): 139 self.__deserialise = importModuleObject(value) 140 141 elif callable(value): 142 self.__deserialise = value 143 else: 144 raise TypeError('Expecting callable for "deserialise"; got %r' % 145 value) 146 147 148 deserialise = property(_getDeserialise, 149 _setDeserialise, 150 doc="callable to de-serialise response from XML " 151 "type") 152 @classmethod 153 def filter_app_factory(cls, app, global_conf, **app_conf): 154 """Set-up using a Paste app factory pattern. Set this method to avoid 155 possible conflicts from multiple inheritance 156 157 @type app: callable following WSGI interface 158 @param app: next middleware application in the chain 159 @type global_conf: dict 160 @param global_conf: PasteDeploy global configuration dictionary 161 @type prefix: basestring 162 @param prefix: prefix for configuration items 163 @type app_conf: dict 164 @param app_conf: PasteDeploy application specific configuration 165 dictionary 166 """ 167 app = cls(app) 168 app.initialise(global_conf, **app_conf) 169 170 return app 171 172 def _getQueryInterfaceKeyName(self): 173 return self.__queryInterfaceKeyName 174 175 def _setQueryInterfaceKeyName(self, value): 176 if not isinstance(value, basestring): 177 raise TypeError('Expecting string type for "queryInterfaceKeyName"' 178 ' got %r' % value) 179 180 self.__queryInterfaceKeyName = value 181 182 queryInterfaceKeyName = property(fget=_getQueryInterfaceKeyName, 183 fset=_setQueryInterfaceKeyName, 184 doc="environ keyname for Attribute Query " 185 "interface") 186 187 @NDGSecurityPathFilter.initCall 188 def __call__(self, environ, start_response): 189 """Check for and parse a SOAP SAML Attribute Query and return a 190 SAML Response 191 192 @type environ: dict 193 @param environ: WSGI environment variables dictionary 194 @type start_response: function 195 @param start_response: standard WSGI start response function 196 """ 197 198 # Ignore non-matching path 199 if not self.pathMatch: 200 return self._app(environ, start_response) 201 202 # Ignore non-POST requests 203 if environ.get('REQUEST_METHOD') != 'POST': 204 return self._app(environ, start_response) 205 206 soapRequestStream = environ.get('wsgi.input') 207 if soapRequestStream is None: 208 raise SOAPQueryInterfaceMiddlewareError('No "wsgi.input" in ' 209 'environ') 210 211 # TODO: allow for chunked data 212 contentLength = environ.get('CONTENT_LENGTH') 213 if contentLength is None: 214 raise SOAPQueryInterfaceMiddlewareError('No "CONTENT_LENGTH" in ' 215 'environ') 216 217 contentLength = int(contentLength) 218 soapRequestTxt = soapRequestStream.read(contentLength) 219 220 # Parse into a SOAP envelope object 221 soapRequest = SOAPEnvelope() 222 soapRequest.parse(StringIO(soapRequestTxt)) 223 224 log.debug("SOAPQueryInterfaceMiddleware.__call__: received SAML " 225 "SOAP AttributeQuery ...") 226 227 queryElem = soapRequest.body.elem[0] 228 229 try: 230 query = self.deserialise(queryElem) 231 except UnknownAttrProfile: 232 log.exception("%r raised parsing incoming query: " % 233 (type(e), traceback.format_exc())) 234 samlResponse = self._makeErrorResponse( 235 StatusCode.UNKNOWN_ATTR_PROFILE_URI) 236 else: 237 # Check for Query Interface in environ 238 queryInterface = environ.get(self.queryInterfaceKeyName) 239 if queryInterface is None: 240 raise SOAPQueryInterfaceMiddlewareConfigError( 241 'No query interface "%s" key found in environ' % 242 self.queryInterfaceKeyName) 243 244 # Call query interface 245 samlResponse = queryInterface(query) 246 247 # Convert to ElementTree representation to enable attachment to SOAP 248 # response body 249 samlResponseElem = self.serialise(samlResponse) 250 251 # Create SOAP response and attach the SAML Response payload 252 soapResponse = SOAPEnvelope() 253 soapResponse.create() 254 soapResponse.body.elem.append(samlResponseElem) 255 256 response = soapResponse.serialize() 257 258 log.debug("SOAPQueryInterfaceMiddleware.__call__: sending response " 259 "...\n\n%s", 260 response) 261 start_response("200 OK", 262 [('Content-length', str(len(response))), 263 ('Content-type', 'text/xml')]) 264 return [response] 265 266 def _makeErrorResponse(self, code): 267 """Convenience method for making a basic response following an error 268 """ 269 samlResponse = Response() 270 271 samlResponse.issueInstant = datetime.utcnow() 272 samlResponse.id = str(uuid4()) 273 274 # Initialise to success status but reset on error 275 samlResponse.status = Status() 276 samlResponse.status.statusCode = StatusCode() 277 samlResponse.status.statusCode.value = code 278 279 return samlResponse 280 -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/saml/attributeinterface.py
r6575 r6578 192 192 if queryInterface is None: 193 193 raise SOAPAttributeInterfaceMiddlewareConfigError( 194 'No query interface "%s" key found in environ' %194 'No query interface "%s" key found in environ' % 195 195 self.queryInterfaceKeyName) 196 196 -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/config/attributeauthority/sitea/siteAUserRoles.py
r6067 r6578 79 79 ISSUER_NAME = "/O=Site A/CN=Attribute Authority" 80 80 81 INSUFFICIENT_PRIVILEGES_REQUESTOR_ID = str(82 X500DN.fromString("/O=Site B/CN=Authorisation Service"))81 INSUFFICIENT_PRIVILEGES_REQUESTOR_ID = X500DN.fromString( 82 "/O=Site B/CN=Authorisation Service") 83 83 84 84 def __init__(self, propertiesFilePath=None): … … 94 94 requestedAttributeNames = [attribute.name 95 95 for attribute in attributeQuery.attributes] 96 if attributeQuery.issuer.format != Issuer.X509_SUBJECT: 97 raise InvalidRequestorId('Requestor issuer format "%s" is invalid' % 98 attributeQuery.issuerFormat.value) 99 96 100 requestorId = X500DN.fromString(attributeQuery.issuer.value) 97 101 -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/__init__.py
r6575 r6578 15 15 from ndg.security.test.unit import BaseTestCase 16 16 17 17 18 class TestApp(object): 19 """Dummy application to terminate middleware stack containing SAML service 20 """ 18 21 def __init__(self, global_conf, **app_conf): 19 22 pass … … 38 41 self.app = paste.fixture.TestApp(wsgiapp) 39 42 40 unittest.TestCase.__init__(self, *args, **kwargs)43 BaseTestCase.__init__(self, *args, **kwargs) -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/attribute-interface.ini
r6575 r6578 21 21 22 22 [filter:SAMLSoapAttributeInterfaceFilter] 23 paste.filter_app_factory = ndg.security.server.wsgi.saml .attributeinterface:SOAPAttributeInterfaceMiddleware.filter_app_factory23 paste.filter_app_factory = ndg.security.server.wsgi.saml:SOAPQueryInterfaceMiddleware.filter_app_factory 24 24 prefix = saml. 25 25 saml.pathMatchList = /attributeauthority/saml 26 26 saml.queryInterfaceKeyName = attributeQueryInterface 27 saml.deserialise = saml.xml.etree:AttributeQueryElementTree.fromXML 28 29 # Specialisation to incorporate ESG Group/Role type 30 saml.serialise = ndg.security.common.saml_utils.esg.xml.etree:EsgResponseElementTree.toXML 27 31 28 32 #______________________________________________________________________________ -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/authz-decision-interface.ini
r6575 r6578 15 15 16 16 [pipeline:main] 17 pipeline = AttributeAuthorityFilter SAMLSoapAttributeInterfaceFilter TestApp17 pipeline = TestAuthorisationServiceFilter SAMLSoapAuthzDecisionInterfaceFilter TestApp 18 18 19 19 [app:TestApp] 20 20 paste.app_factory = ndg.security.test.unit.wsgi.saml:TestApp 21 21 22 [filter:SAMLSoapA ttributeInterfaceFilter]23 paste.filter_app_factory = ndg.security.server.wsgi.saml .attributeinterface:SOAPAttributeInterfaceMiddleware.filter_app_factory22 [filter:SAMLSoapAuthzDecisionInterfaceFilter] 23 paste.filter_app_factory = ndg.security.server.wsgi.saml:SOAPQueryInterfaceMiddleware.filter_app_factory 24 24 prefix = saml. 25 saml.pathMatchList = /attributeauthority/saml 26 saml.queryInterfaceKeyName = attributeQueryInterface 25 saml.pathMatchList = /authorisationservice 26 saml.queryInterfaceKeyName = AUTHZ_DECISION_QUERY_FUNC 27 saml.deserialise = saml.xml.etree:AuthzDecisionQueryElementTree.fromXML 28 saml.serialise = saml.xml.etree:ResponseElementTree.toXML 27 29 28 30 #______________________________________________________________________________ 29 # A ttribute AuthorityWSGI settings31 # Authorisation Service WSGI settings 30 32 # 31 [filter: AttributeAuthorityFilter]33 [filter:TestAuthorisationServiceFilter] 32 34 # This filter is a container for a binding to a SOAP based interface to the 33 35 # Attribute Authority 34 paste.filter_app_factory = ndg.security.server.wsgi.attributeauthority:AttributeAuthorityMiddleware.filter_app_factory 35 36 prefix = attributeAuthority. 37 38 attributeAuthority.environKeyName: attributeauthority 39 attributeAuthority.environKeyNameAttributeQueryInterface: attributeQueryInterface 40 41 # Attribute Authority settings 42 # 'name' setting MUST agree with map config file 'thisHost' name attribute 43 attributeAuthority.name: Site A 44 45 # Lifetime is measured in seconds 46 attributeAuthority.attCertLifetime: 28800 47 48 # Allow an offset for clock skew between servers running 49 # security services. NB, measured in seconds - use a minus sign for time in the 50 # past 51 attributeAuthority.attCertNotBeforeOff: 0 52 53 # All Attribute Certificates issued are recorded in this dir 54 attributeAuthority.attCertDir: %(testConfigDir)s/attributeauthority/sitea/attributeCertificateLog 55 56 # Files in attCertDir are stored using a rotating file handler 57 # attCertFileLogCnt sets the max number of files created before the first is 58 # overwritten 59 attributeAuthority.attCertFileName: ac.xml 60 attributeAuthority.attCertFileLogCnt: 16 61 attributeAuthority.dnSeparator:/ 62 63 # Location of role mapping file 64 attributeAuthority.mapConfigFilePath: %(testConfigDir)s/attributeauthority/sitea/siteAMapConfig.xml 65 66 # Settings for custom AttributeInterface derived class to get user roles for given 67 # user ID 68 attributeAuthority.attributeInterface.modFilePath: %(testConfigDir)s/attributeauthority/sitea 69 attributeAuthority.attributeInterface.modName: ndg.security.test.config.attributeauthority.sitea.siteAUserRoles 70 attributeAuthority.attributeInterface.className: TestUserRoles 71 72 # Config for XML signature of Attribute Certificate 73 attributeAuthority.signingPriKeyFilePath: %(testConfigDir)s/attributeauthority/sitea/siteA-aa.key 74 attributeAuthority.signingCertFilePath: %(testConfigDir)s/attributeauthority/sitea/siteA-aa.crt 75 attributeAuthority.caCertFilePathList: %(testConfigDir)s/ca/ndg-test-ca.crt 36 paste.filter_app_factory = ndg.security.test.unit.wsgi.saml.test_soapauthzdecisioninterface:TestAuthorisationServiceMiddleware 37 queryInterfaceKeyName = AUTHZ_DECISION_QUERY_FUNC -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/test_soapattributeinterface.py
r6574 r6578 10 10 __contact__ = "Philip.Kershaw@stfc.ac.uk" 11 11 __revision__ = '$Id: $' 12 import unittest 12 13 from uuid import uuid4 13 14 from datetime import datetime … … 22 23 from ndg.security.common.soap.etree import SOAPEnvelope 23 24 from ndg.security.common.saml_utils.esg import EsgSamlNamespaces 24 25 26 class SOAPAttributeInterfaceMiddlewareTestCase(unittest.TestCase): 25 from ndg.security.test.unit.wsgi.saml import SoapSamlInterfaceMiddlewareTestCase 26 27 28 class SOAPAttributeInterfaceMiddlewareTestCase( 29 SoapSamlInterfaceMiddlewareTestCase): 27 30 CONFIG_FILENAME = 'attribute-interface.ini' 28 31 … … 179 182 StatusCode.INVALID_ATTR_NAME_VALUE_URI) 180 183 181 def test04Invalid Issuer(self):182 request = self._makeRequest(issuer=" My Attribute Query Issuer")184 def test04InvalidQueryIssuer(self): 185 request = self._makeRequest(issuer="/CN=My Attribute Query Issuer") 183 186 184 187 header = { -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/test_soapauthzdecisioninterface.py
r6575 r6578 1 1 #!/usr/bin/env python 2 """Unit tests for WSGI SAML 2.0 SOAP A ttributeQuery Interface2 """Unit tests for WSGI SAML 2.0 SOAP Authorisation Decision Query Interface 3 3 4 4 NERC DataGrid Project 5 5 """ 6 6 __author__ = "P J Kershaw" 7 __date__ = " 21/08/09"8 __copyright__ = "(C) 20 09Science and Technology Facilities Council"7 __date__ = "15/02/2010" 8 __copyright__ = "(C) 2010 Science and Technology Facilities Council" 9 9 __license__ = "BSD - see LICENSE file in top-level directory" 10 10 __contact__ = "Philip.Kershaw@stfc.ac.uk" 11 11 __revision__ = '$Id: $' 12 import unittest 12 13 from uuid import uuid4 13 from datetime import datetime 14 from datetime import datetime, timedelta 14 15 from cStringIO import StringIO 15 16 16 from saml.saml2.core import ( Attribute, SAMLVersion, Subject, NameID, Issuer,17 A ttributeQuery, XSStringAttributeValue,18 StatusCode )19 from saml.xml import XMLConstants 20 from saml.xml.etree import A ttributeQueryElementTree, ResponseElementTree17 from saml.saml2.core import (SAMLVersion, Subject, NameID, Issuer, Response, 18 AuthzDecisionQuery, AuthzDecisionStatement, Status, 19 StatusCode, StatusMessage, DecisionType, Action, 20 Conditions, Assertion) 21 from saml.xml.etree import AuthzDecisionQueryElementTree, ResponseElementTree 21 22 22 23 from ndg.security.common.soap.etree import SOAPEnvelope … … 25 26 26 27 27 class SOAPAuthzDecisionInterfaceMiddlewareTestCase(unittest.TestCase): 28 class TestAuthorisationServiceMiddleware(object): 29 """Test Authorisation Service interface stub""" 30 QUERY_INTERFACE_KEYNAME_OPTNAME = 'queryInterfaceKeyName' 31 RESOURCE_URI = 'http://localhost/dap/data/' 32 ISSUER_DN = '/O=Site A/CN=PDP' 33 34 def __init__(self, app, global_conf, **app_conf): 35 self.queryInterfaceKeyName = app_conf[ 36 TestAuthorisationServiceMiddleware.QUERY_INTERFACE_KEYNAME_OPTNAME] 37 self._app = app 38 39 def __call__(self, environ, start_response): 40 environ[self.queryInterfaceKeyName] = self.authzDecisionQueryFactory() 41 return self._app(environ, start_response) 42 43 def authzDecisionQueryFactory(self): 44 def authzDecisionQuery(query): 45 response = Response() 46 now = datetime.utcnow() 47 response.issueInstant = now 48 49 # Make up a request ID that this response is responding to 50 response.inResponseTo = query.id 51 response.id = str(uuid4()) 52 response.version = SAMLVersion(SAMLVersion.VERSION_20) 53 54 response.issuer = Issuer() 55 response.issuer.format = Issuer.X509_SUBJECT 56 response.issuer.value = TestAuthorisationServiceMiddleware.ISSUER_DN 57 58 response.status = Status() 59 response.status.statusCode = StatusCode() 60 response.status.statusCode.value = StatusCode.SUCCESS_URI 61 response.status.statusMessage = StatusMessage() 62 response.status.statusMessage.value = \ 63 "Response created successfully" 64 65 assertion = Assertion() 66 assertion.version = SAMLVersion(SAMLVersion.VERSION_20) 67 assertion.id = str(uuid4()) 68 assertion.issueInstant = now 69 70 authzDecisionStatement = AuthzDecisionStatement() 71 authzDecisionStatement.decision = DecisionType.PERMIT 72 authzDecisionStatement.resource = \ 73 TestAuthorisationServiceMiddleware.RESOURCE_URI 74 authzDecisionStatement.actions.append(Action()) 75 authzDecisionStatement.actions[-1].namespace = Action.GHPP_NS_URI 76 authzDecisionStatement.actions[-1].value = Action.HTTP_GET_ACTION 77 assertion.authzDecisionStatements.append(authzDecisionStatement) 78 79 # Add a conditions statement for a validity of 8 hours 80 assertion.conditions = Conditions() 81 assertion.conditions.notBefore = now 82 assertion.conditions.notOnOrAfter = now + timedelta(seconds=60*60*8) 83 84 assertion.subject = Subject() 85 assertion.subject.nameID = NameID() 86 assertion.subject.nameID.format = query.subject.nameID.format 87 assertion.subject.nameID.value = query.subject.nameID.value 88 89 assertion.issuer = Issuer() 90 assertion.issuer.format = Issuer.X509_SUBJECT 91 assertion.issuer.value = \ 92 TestAuthorisationServiceMiddleware.ISSUER_DN 93 94 response.assertions.append(assertion) 95 return response 96 97 return authzDecisionQuery 98 99 100 class SOAPAuthzDecisionInterfaceMiddlewareTestCase( 101 SoapSamlInterfaceMiddlewareTestCase): 28 102 CONFIG_FILENAME = 'authz-decision-interface.ini' 29 103 30 104 def _createAuthzDecisionQuery(self, 31 issuer="/O=Site A/CN=Authorisation Service", 32 subject="https://openid.localhost/philip.kershaw"): 33 query = AttributeQuery() 105 issuer="/O=Site A/CN=PEP", 106 subject="https://openid.localhost/philip.kershaw", 107 resource=TestAuthorisationServiceMiddleware.RESOURCE_URI, 108 action=Action.HTTP_GET_ACTION, 109 actionNs=Action.GHPP_NS_URI): 110 query = AuthzDecisionQuery() 34 111 query.version = SAMLVersion(SAMLVersion.VERSION_20) 35 112 query.id = str(uuid4()) … … 45 122 query.subject.nameID.value = subject 46 123 47 48 # special case handling for 'FirstName' attribute 49 fnAttribute = Attribute() 50 fnAttribute.name = EsgSamlNamespaces.FIRSTNAME_ATTRNAME 51 fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string" 52 fnAttribute.friendlyName = "FirstName" 53 54 query.attributes.append(fnAttribute) 55 56 # special case handling for 'LastName' attribute 57 lnAttribute = Attribute() 58 lnAttribute.name = EsgSamlNamespaces.LASTNAME_ATTRNAME 59 lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string" 60 lnAttribute.friendlyName = "LastName" 61 62 query.attributes.append(lnAttribute) 63 64 # special case handling for 'LastName' attribute 65 emailAddressAttribute = Attribute() 66 emailAddressAttribute.name = EsgSamlNamespaces.EMAILADDRESS_ATTRNAME 67 emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\ 68 XSStringAttributeValue.TYPE_LOCAL_NAME 69 emailAddressAttribute.friendlyName = "emailAddress" 70 71 query.attributes.append(emailAddressAttribute) 124 query.resource = resource 125 query.actions.append(Action()) 126 query.actions[0].namespace = actionNs 127 query.actions[0].value = action 72 128 73 129 return query … … 79 135 query = self._createAuthzDecisionQuery(**kw) 80 136 81 elem = AuthzDec usionQueryElementTree.toXML(query)137 elem = AuthzDecisionQueryElementTree.toXML(query) 82 138 soapRequest = SOAPEnvelope() 83 139 soapRequest.create() … … 115 171 'Content-type': 'text/xml' 116 172 } 117 response = self.app.post('/a ttributeauthority/saml',173 response = self.app.post('/authorisationservice/', 118 174 params=request, 119 175 headers=header, … … 127 183 self.assert_(samlResponse.assertions[0].subject.nameID.value == \ 128 184 query.subject.nameID.value) 129 130 def test02AttributeReleaseDenied(self): 131 request = self._makeRequest(issuer="/O=Site B/CN=Authorisation Service") 185 self.assert_(samlResponse.assertions[0]) 186 self.assert_(samlResponse.assertions[0].authzDecisionStatements[0]) 187 self.assert_(samlResponse.assertions[0].authzDecisionStatements[0 188 ].decision == DecisionType.PERMIT) 132 189 133 header = { 134 'soapAction': "http://www.oasis-open.org/committees/security", 135 'Content-length': str(len(request)), 136 'Content-type': 'text/xml' 137 } 138 139 response = self.app.post('/attributeauthority/saml', 140 params=request, 141 headers=header, 142 status=200) 143 144 print("Response status=%d" % response.status) 145 146 samlResponse = self._getSAMLResponse(response.body) 147 148 self.assert_(samlResponse.status.statusCode.value == \ 149 StatusCode.INVALID_ATTR_NAME_VALUE_URI) 150 151 def test03InvalidAttributesRequested(self): 152 query = self._createAuthzDecisionQuery() 153 154 # Add an unsupported Attribute name 155 attribute = Attribute() 156 attribute.name = "urn:my:attribute" 157 attribute.nameFormat = XMLConstants.XSD_NS+"#"+\ 158 XSStringAttributeValue.TYPE_LOCAL_NAME 159 attribute.friendlyName = "myAttribute" 160 query.attributes.append(attribute) 161 162 request = self._makeRequest(query=query) 163 164 header = { 165 'soapAction': "http://www.oasis-open.org/committees/security", 166 'Content-length': str(len(request)), 167 'Content-type': 'text/xml' 168 } 169 170 response = self.app.post('/attributeauthority/saml', 171 params=request, 172 headers=header, 173 status=200) 174 175 print("Response status=%d" % response.status) 176 177 samlResponse = self._getSAMLResponse(response.body) 178 179 self.assert_(samlResponse.status.statusCode.value == \ 180 StatusCode.INVALID_ATTR_NAME_VALUE_URI) 181 182 def test04InvalidIssuer(self): 183 request = self._makeRequest(issuer="My Attribute Query Issuer") 184 185 header = { 186 'soapAction': "http://www.oasis-open.org/committees/security", 187 'Content-length': str(len(request)), 188 'Content-type': 'text/xml' 189 } 190 191 response = self.app.post('/attributeauthority/saml', 192 params=request, 193 headers=header, 194 status=200) 195 196 print("Response status=%d" % response.status) 197 198 samlResponse = self._getSAMLResponse(response.body) 199 200 self.assert_(samlResponse.status.statusCode.value == \ 201 StatusCode.REQUEST_DENIED_URI) 202 203 def test05UnknownPrincipal(self): 204 request = self._makeRequest(subject="Joe.Bloggs") 205 206 header = { 207 'soapAction': "http://www.oasis-open.org/committees/security", 208 'Content-length': str(len(request)), 209 'Content-type': 'text/xml' 210 } 211 212 response = self.app.post('/attributeauthority/saml', 213 params=request, 214 headers=header, 215 status=200) 216 217 print("Response status=%d" % response.status) 218 219 samlResponse = self._getSAMLResponse(response.body) 220 221 self.assert_(samlResponse.status.statusCode.value == \ 222 StatusCode.UNKNOWN_PRINCIPAL_URI) 223 190 def test02(self): 191 pass 224 192 225 193 if __name__ == "__main__":
Note: See TracChangeset
for help on using the changeset viewer.