Changeset 6686
- Timestamp:
- 05/03/10 16:56:35 (11 years ago)
- Location:
- TI12-security/trunk/NDGSecurity/python
- Files:
-
- 1 deleted
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
TI12-security/trunk/NDGSecurity/python/.pydevproject
r6673 r6686 5 5 <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> 6 6 <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property> 7 <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> 8 <path>/ndg_security_python/ndg_security_common</path> 9 <path>/ndg_security_python/ndg_security_server</path> 10 <path>/ndg_security_python/ndg_security_test</path> 11 </pydev_pathproperty> 12 <pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH"> 13 <path>/home/pjkersha/workspace/ndg_saml</path> 14 <path>/home/pjkersha/workspace/MyProxyClient</path> 15 <path>/home/pjkersha/workspace/AuthKit/trunk</path> 16 </pydev_pathproperty> 7 17 </pydev_project> -
TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/utils/factory.py
r6572 r6686 4 4 NERC DataGrid project 5 5 """ 6 __author__ = " C Byrom - Tessella"7 __date__ = " 28/08/08"8 __copyright__ = "(C) 20 09Science and Technology Facilities Council"6 __author__ = "Philip Kershaw" 7 __date__ = "15/02/10" 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" … … 28 28 if objectName is None: 29 29 if ':' in moduleName: 30 # Support P Aste style import syntax with rhs of colon denoting30 # Support Paste style import syntax with rhs of colon denoting 31 31 # module content to import 32 32 _moduleName, objectName = moduleName.rsplit(':', 1) -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/attributeauthority.py
r6673 r6686 36 36 37 37 from ndg.security.common.saml_utils.esg import EsgSamlNamespaces 38 from ndg.security.common.X509 import X500DN 38 39 from ndg.security.common.utils import TypedList 39 40 from ndg.security.common.utils.classfactory import instantiateClass … … 54 55 def __init__(self, msg): 55 56 log.error(msg) 56 Exception.__init__(self, msg) 57 58 59 class AttributeAuthorityAccessDenied(AttributeAuthorityError): 60 """NDG Attribute Authority - access denied exception. 61 62 Raise from getAttCert method where no roles are available for the user 63 but that the request is otherwise valid. In all other error cases raise 64 AttributeAuthorityError""" 65 66 67 class AttributeAuthorityNoTrustedHosts(AttributeAuthorityError): 68 """Raise from getTrustedHosts if there are no trusted hosts defined in 69 the map configuration""" 70 71 72 class AttributeAuthorityNoMatchingRoleInTrustedHosts(AttributeAuthorityError): 73 """Raise from getTrustedHosts if there is no mapping to any of the 74 trusted hosts for the given input role name""" 57 Exception.__init__(self, msg) 75 58 76 59 … … 112 95 attributeInterfacePropertyDefaults = { 113 96 'modFilePath': '', 114 'modName': '',115 97 'className': '' 116 98 } … … 120 102 # in the config 121 103 propertyDefaults = { 122 'name': '', 123 'attCertLifetime': -1, 124 'attCertNotBeforeOff': 0., 125 'clockSkew': timedelta(seconds=0.), 104 'issuerName': '', 105 'assertionLifetime': -1, 126 106 'dnSeparator': '/', 127 107 ATTRIBUTE_INTERFACE_KEYNAME: attributeInterfacePropertyDefaults 128 108 } 129 130 mapConfigHostDefaults = {131 'siteName': None,132 'aaURI': NotImplemented,133 'aaDN': NotImplemented,134 'loginURI': NotImplemented,135 'loginServerDN': NotImplemented,136 'loginRequestServerDN': NotImplemented137 }138 109 139 110 def __init__(self): … … 145 116 setattr(self, '_AttributeAuthority__%s' % name, val) 146 117 147 self.__caCertFilePathList = TypedList(basestring)148 149 118 self.__propFilePath = None 150 119 self.__propFileSection = 'DEFAULT' 151 120 self.__propPrefix = '' 152 153 self.__cert = None154 155 # Issuer details - serialise using the separator string set in the156 # properties file157 self.__issuer = None158 self.__issuerSerialNumber = None159 self.__attCertLog = None160 self.__name = None161 121 162 122 self.__attributeInterfaceCfg = {} 163 123 164 def _getCert(self): 165 return self.__cert 166 167 def _getIssuer(self): 168 return self.__issuer 169 170 def _getIssuerSerialNumber(self): 171 return self.__issuerSerialNumber 172 173 def _getName(self): 174 return self.__name 175 176 def _getAttCertLifetime(self): 177 return self.__attCertLifetime 178 179 def _getAttCertNotBeforeOff(self): 180 return self.__attCertNotBeforeOff 181 182 def _getClockSkew(self): 183 return self.__clockSkew 124 def _getAssertionLifetime(self): 125 return self.__assertionLifetime 184 126 185 127 def _getAttributeInterface(self): 186 128 return self.__attributeInterface 187 129 188 def _setCert(self, value): 189 if not isinstance(value, X509Cert): 190 raise TypeError('Expecting %r type for "cert"; got %r' % 191 (X509Cert, type(value))) 192 193 self.__cert = value 194 195 def _setIssuer(self, value): 196 self.__issuer = value 197 198 def _setIssuerSerialNumber(self, value): 199 if not isinstance(value, (long, int)): 200 raise TypeError('Expecting long or int type for "name"; got %r' % 201 type(value)) 202 self.__issuerSerialNumber = value 203 204 def _setName(self, value): 205 if not isinstance(value, basestring): 206 raise TypeError('Expecting string type for "name"; got %r' % 207 type(value)) 208 self.__name = value 209 210 def _setAttCertLifetime(self, value): 130 def _setAssertionLifetime(self, value): 211 131 if isinstance(value, float): 212 self.__a ttCertLifetime = value132 self.__assertionLifetime = value 213 133 214 134 elif isinstance(value, (basestring, int, long)): 215 self.__a ttCertLifetime = float(value)135 self.__assertionLifetime = float(value) 216 136 else: 217 137 raise TypeError('Expecting float, int, long or string type for ' 218 '"attCertLifetime"; got %r' % type(value)) 219 220 def _setAttCertNotBeforeOff(self, value): 221 if isinstance(value, float): 222 self.__attCertNotBeforeOff = value 223 224 elif isinstance(value, (basestring, int, long)): 225 self.__attCertNotBeforeOff = float(value) 226 else: 227 raise TypeError('Expecting float, int, long or string type for ' 228 '"attCertNotBeforeOff"; got %r' % type(value)) 229 230 def _setClockSkew(self, value): 231 if isinstance(value, (float, int, long)): 232 self.__clockSkew = timedelta(seconds=value) 233 234 elif isinstance(value, basestring): 235 self.__clockSkew = timedelta(seconds=float(value)) 236 else: 237 raise TypeError('Expecting float, int, long or string type for ' 238 '"clockSkew"; got %r' % type(value)) 138 '"assertionLifetime"; got %r' % type(value)) 239 139 240 140 def _setAttributeInterface(self, value): … … 245 145 246 146 self.__attributeInterface = value 247 248 def _get_caCertFilePathList(self):249 return self.__caCertFilePathList250 251 def _set_caCertFilePathList(self, val):252 if not isinstance(val, (list, tuple)):253 raise TypeError('Expecting list or tuple type for '254 '"caCertFilePathList"; got %r' % type(val))255 256 # Overwrite any original settings257 self.__caCertFilePathList = TypedList(basestring)258 259 # Update with new items260 self.__caCertFilePathList += val261 262 caCertFilePathList = property(fget=_get_caCertFilePathList,263 fset=_set_caCertFilePathList,264 doc="list of file paths for CA certificates "265 "used to validate an Attribute "266 "Certificate")267 147 268 148 def _get_attributeInterfaceCfg(self): … … 395 275 doc="Set a prefix for ini file properties") 396 276 397 cert = property(fget=_getCert, 398 fset=_setCert, 399 doc="X.509 Issuer Certificate") 400 401 issuer = property(fget=_getIssuer, 402 fset=_setIssuer, 403 doc="Issuer name") 404 405 issuerSerialNumber = property(fget=_getIssuerSerialNumber, 406 fset=_setIssuerSerialNumber, 407 doc="Issuer Serial Number") 408 409 name = property(fget=_getName, 410 fset=_setName, 411 doc="Issuer organisation name") 412 413 attCertLifetime = property(fget=_getAttCertLifetime, 414 fset=_setAttCertLifetime, 415 doc="Attribute certificate lifetime") 416 417 attCertNotBeforeOff = property(fget=_getAttCertNotBeforeOff, 418 fset=_setAttCertNotBeforeOff, 419 doc="Attribute certificate clock skew in " 420 "seconds") 421 422 clockSkew = property(fget=_getClockSkew, 423 fset=_setClockSkew, 424 doc="Allow a clock skew in seconds for SAML Attribute" 425 " Query issueInstant parameter check") 277 assertionLifetime = property(fget=_getAssertionLifetime, 278 fset=_setAssertionLifetime, 279 doc="validity lifetime (s) for Attribute " 280 "assertions issued") 426 281 427 282 attributeInterface = property(fget=_getAttributeInterface, 428 283 fset=_setAttributeInterface, 429 284 doc="Attribute Interface object") 430 431 name = property(fget=_getName, fset=_setName, doc="Organisation Name")432 285 433 286 @classmethod 434 287 def fromPropertyFile(cls, propFilePath=None, propFileSection='DEFAULT', 435 propPrefix='attributeauthority.', 436 bReadMapConfig=True): 288 propPrefix='attributeauthority.'): 437 289 """Create new NDG Attribute Authority instance from the property file 438 290 settings … … 463 315 attributeAuthority.propFilePath = propFilePath 464 316 attributeAuthority.readProperties() 465 attributeAuthority.initialise( bReadMapConfig=bReadMapConfig)317 attributeAuthority.initialise() 466 318 467 319 return attributeAuthority 468 320 469 321 @classmethod 470 def fromProperties(cls, propPrefix='attributeauthority.', 471 bReadMapConfig=True, **prop): 322 def fromProperties(cls, propPrefix='attributeauthority.', **prop): 472 323 """Create new NDG Attribute Authority instance from input property 473 324 keywords … … 477 328 property names - useful where properties are being parsed from a file 478 329 section containing parameter names for more than one application 479 @type bReadMapConfig: boolean480 @param bReadMapConfig: by default the Map Configuration file is481 read. Set this flag to False to override.482 330 """ 483 331 attributeAuthority = AttributeAuthority() … … 486 334 487 335 attributeAuthority.setProperties(**prop) 488 attributeAuthority.initialise( bReadMapConfig=bReadMapConfig)336 attributeAuthority.initialise() 489 337 490 338 return attributeAuthority 491 339 492 def initialise(self , bReadMapConfig=True):340 def initialise(self): 493 341 """Convenience method for set up of Attribute Interface, map 494 342 configuration and PKI""" 495 496 # Read the Map Configuration file497 if bReadMapConfig:498 self.readMapConfig()499 343 500 344 # Instantiate Certificate object 501 345 log.debug("Reading and checking Attribute Authority X.509 cert. ...") 502 self.cert = X509Cert.Read(self.signingCertFilePath)503 504 # Check it's valid505 try:506 self.cert.isValidTime(raiseExcep=True)507 508 except Exception, e:509 raise AttributeAuthorityError("Attribute Authority's certificate "510 "is invalid: %s" % e)511 512 # Check CA certificate513 log.debug("Reading and checking X.509 CA certificate ...")514 for caCertFile in self.caCertFilePathList:515 caCert = X509Cert(caCertFile)516 caCert.read()517 518 try:519 caCert.isValidTime(raiseExcep=True)520 521 except Exception, e:522 raise AttributeAuthorityError('CA certificate "%s" is '523 'invalid: %s'% (caCert.dn, e))524 525 # Issuer details - serialise using the separator string set in the526 # properties file527 self.issuer = self.cert.dn.serialise(separator=self.dnSeparator)528 529 self.issuerSerialNumber = self.cert.serialNumber530 346 531 347 # Load user - user attribute look-up plugin 532 348 self.initAttributeInterface() 533 534 attCertFilePath = os.path.join(self.attCertDir, self.attCertFileName)535 349 536 350 def setProperties(self, **prop): … … 594 408 classProperties.update(self.attributeInterfaceCfg) 595 409 596 modName = classProperties.pop('modName') 597 className = classProperties.pop('className') 410 className = classProperties.pop('className', None) 411 if className is None: 412 raise AttributeAuthorityConfigError('No Attribute Interface ' 413 '"className" property set') 598 414 599 415 # file path may be omitted … … 724 540 725 541 return samlResponse 726 727 def getRoles(self, userId): 728 """Get the roles available to the registered user identified userId. 729 730 @type dn: string 731 @param dn: user identifier - could be a X500 Distinguished Name 732 @return: list of roles for the given user ID""" 733 734 log.debug('Calling getRoles for user "%s" ...' % userId) 735 736 # Call to AttributeInterface derived class. Each Attribute Authority 737 # should define it's own roles class derived from AttributeInterface to 738 # define how roles are accessed 739 try: 740 return self.__attributeInterface.getRoles(userId) 741 742 except Exception, e: 743 raise AttributeAuthorityError("Getting user roles: %s" % e) 744 745 def getAttCertFactory(self): 542 543 def samlAttributeQueryFactory(self): 746 544 """Factory method to create SAML Attribute Query wrapper function 747 @rtype: function748 @return getAttCert method function wrapper749 """750 def getAttCertWrapper(*arg, **kw):751 """752 @type *arg: tuple753 @param *arg: getAttCert arguments754 @type **kw: dict755 @param **kw: getAttCert keyword arguments756 @rtype: ndg.security.common.AttCert.AttCert757 @return: new attribute certificate758 """759 return self.getAttCert(*arg, **kw)760 761 return getAttCertWrapper762 763 def samlAttributeQueryFactory(self):764 """Factory method to create SAML Attribute Qeury wrapper function765 545 @rtype: function 766 546 @return: samlAttributeQuery method function wrapper -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/attributeauthority.py
r6586 r6686 15 15 from ndg.security.server.attributeauthority import AttributeAuthority 16 16 from ndg.security.server.wsgi import NDGSecurityMiddlewareBase 17 from ndg.security.server.wsgi.zsi import SOAPBindingMiddleware18 17 19 18 … … 173 172 doc="Attribute Authority SAML attribute query " 174 173 "function") 175 176 177 from ndg.security.server.zsi.attributeauthority import AttributeAuthorityWS178 179 class AttributeAuthoritySOAPBindingMiddlewareConfigError(Exception):180 """Raise if a configuration problem is found"""181 182 183 class AttributeAuthoritySOAPBindingMiddleware(NDGSecurityMiddlewareBase,184 AttributeAuthorityWS):185 """Inheritance from NDGSecurityMiddlewareBase provides a __call__186 implementation which sets a reference to environ as an object attribute.187 188 Inheritance from AttributeAuthorityWS enables preservation of the same189 SOAP callbacks but with the190 ndg.security.server.attributeauthority.AttributeAuthority instance provided191 from environ192 193 @type DEFAULT_ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME: basestring194 @cvar DEFAULT_ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME: Key name used to index the195 ndg.security.server.attributeauthority.AttributeAuthority instance in the196 environ dictionary197 @type ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME_CFG_OPTNAME: basestring198 @cvar ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME_CFG_OPTNAME: configuration option name199 for the attribute authority environ key200 @type DEFAULT_ENVIRON_KEYNAME: basestring201 @cvar DEFAULT_ENVIRON_KEYNAME: default value for Key name used to index202 THIS SOAP Service Binding middleware instance in the environ dictionary203 @type ENVIRON_KEYNAME_CFG_OPTNAME: basestring204 @cvar ENVIRON_KEYNAME_CFG_OPTNAME: configuration option name for this205 middleware's environ key206 """207 DEFAULT_ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME = \208 "ndg.security.server.attributeauthority.AttributeAuthority"209 ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME_CFG_OPTNAME = \210 'attributeAuthorityEnvironKeyName'211 212 DEFAULT_ENVIRON_KEYNAME = ("ndg.security.server.wsgi.attributeauthority."213 "AttributeAuthoritySOAPBindingMiddleware")214 ENVIRON_KEYNAME_CFG_OPTNAME = 'environKeyName'215 216 def __init__(self, app):217 """Don't call AttributeAuthorityWS.__init__ - AttributeAuthority218 instance is provided via environ through upstream219 AttributeAuthorityMiddleware220 """221 # Call this base class initialiser to set-up the environ attribute222 NDGSecurityMiddlewareBase.__init__(self, app, None)223 AttributeAuthorityWS.__init__(self)224 225 self.__keyName = None226 self.__attributeAuthorityKeyName = None227 228 def _getKeyName(self):229 return self.__keyName230 231 def _setKeyName(self, val):232 if not isinstance(val, basestring):233 raise TypeError('Expecting %r for "keyName" attribute; got %r' %234 (basestring, type(val)))235 self.__keyName = val236 237 keyName = property(fget=_getKeyName,238 fset=_setKeyName,239 doc="Key name used to index THIS SOAP Service Binding "240 "middleware instance in the environ dictionary")241 242 def _getAttributeAuthorityKeyName(self):243 return self.__attributeAuthorityKeyName244 245 def _setAttributeAuthorityKeyName(self, val):246 if not isinstance(val, basestring):247 raise TypeError('Expecting %r for "attributeAuthorityKeyName" '248 'attribute; got %r' %(basestring, type(val)))249 self.__attributeAuthorityKeyName = val250 251 attributeAuthorityKeyName = property(fget=_getAttributeAuthorityKeyName,252 fset=_setAttributeAuthorityKeyName,253 doc="Key name used to index the "254 "ndg.security.server.attribute"255 "authority.AttributeAuthority "256 "instance in the environ "257 "dictionary")258 259 @classmethod260 def filter_app_factory(cls, app, global_conf,261 attributeAuthoritySOAPBindingPrefix='attributeauthority.soapbinding.',262 **app_conf):263 """Set-up Attribute Authority SOAP Binding middleware using a Paste app264 factory pattern. Overloaded base class method to enable custom265 settings from app_conf266 267 @type app: callable following WSGI interface268 @param app: next middleware application in the chain269 @type global_conf: dict270 @param global_conf: PasteDeploy global configuration dictionary271 @type prefix: basestring272 @param prefix: prefix for configuration items273 @type app_conf: dict274 @param app_conf: PasteDeploy application specific configuration275 dictionary276 """277 # Generic Binding middleware intercepts the SOAP_ACTION set in environ278 # and maps it to the matching soap_{SOAP_ACTION} method from this class279 soapBindingApp = SOAPBindingMiddleware.filter_app_factory(app,280 global_conf,281 **app_conf)282 283 # Make the SOAP Binding wrapper pick up this Attribute Authority284 # specific SOAP Binding285 optName = attributeAuthoritySOAPBindingPrefix + \286 cls.ENVIRON_KEYNAME_CFG_OPTNAME287 soapBindingApp.serviceSOAPBindingKeyName = app_conf.get(optName,288 cls.DEFAULT_ENVIRON_KEYNAME)289 290 # Instantiate this middleware and copy the environ key name setting for291 # the Attribute Authority Service SOAP Binding292 app = cls(soapBindingApp)293 app.keyName = soapBindingApp.serviceSOAPBindingKeyName294 295 # envrion key name for the296 # ndg.security.server.attributeauthority.AttributeAuthority instance297 optName = attributeAuthoritySOAPBindingPrefix + \298 cls.ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME_CFG_OPTNAME299 300 app.attributeAuthorityKeyName = app_conf.get(optName,301 cls.DEFAULT_ATTRIBUTE_AUTHORITY_ENVIRON_KEYNAME)302 303 304 # Extract local WS-Security signature verification filter305 optName = attributeAuthoritySOAPBindingPrefix + \306 cls.WSSE_SIGNATURE_VERIFICATION_FILTER_ID_OPTNAME307 app.wsseSignatureVerificationFilterID = app_conf.pop(optName, None)308 if app.wsseSignatureVerificationFilterID is None:309 log.warning('No "%s" option was set in the input config' %310 cls.WSSE_SIGNATURE_VERIFICATION_FILTER_ID_OPTNAME)311 else:312 log.info('Updated setting from "%s" option' %313 cls.WSSE_SIGNATURE_VERIFICATION_FILTER_ID_OPTNAME)314 315 return app316 317 @NDGSecurityMiddlewareBase.initCall318 def __call__(self, environ, start_response):319 """Set a reference to self in environ for the SOAPBindingMiddleware320 instance to pick up downstream321 322 @type environ: dict323 @param environ: WSGI environment variables dictionary324 @type start_response: function325 @param start_response: standard WSGI start response function326 """327 328 environ[self.keyName] = self329 return self._app(environ, start_response)330 331 def soap_getAttCert(self, ps):332 '''Retrieve an Attribute Certificate333 334 @type ps: ZSI ParsedSoap335 @param ps: client SOAP message336 @rtype: ndg.security.common.zsi.attributeauthority.AttributeAuthority_services_types.getAttCertResponse_Holder337 @return: response'''338 self._setAttributeAuthorityFromEnviron()339 return AttributeAuthorityWS.soap_getAttCert(self, ps)340 341 def soap_getHostInfo(self, ps):342 '''Get information about this host343 344 @type ps: ZSI ParsedSoap345 @param ps: client SOAP message346 @rtype: response347 @return: response'''348 self._setAttributeAuthorityFromEnviron()349 return AttributeAuthorityWS.soap_getHostInfo(self, ps)350 351 def soap_getAllHostsInfo(self, ps):352 '''Get information about all hosts353 354 @type ps: ZSI ParsedSoap355 @param ps: client SOAP message356 @rtype: tuple357 @return: response object'''358 self._setAttributeAuthorityFromEnviron()359 return AttributeAuthorityWS.soap_getAllHostsInfo(self, ps)360 361 def soap_getTrustedHostInfo(self, ps):362 '''Get information about other trusted hosts363 364 @type ps: ZSI ParsedSoap365 @param ps: client SOAP message366 @rtype: tuple367 @return: response object'''368 self._setAttributeAuthorityFromEnviron()369 return AttributeAuthorityWS.soap_getTrustedHostInfo(self, ps)370 371 def _setAttributeAuthorityFromEnviron(self):372 self.aa = self.environ.get(self.attributeAuthorityKeyName)373 if self.aa is None:374 raise AttributeAuthoritySOAPBindingMiddlewareConfigError(375 'No "%s" key found in environ' %376 self.attributeAuthorityKeyName) -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/saml/__init__.py
r6615 r6686 89 89 self.__serialise = None 90 90 self.__deserialise = None 91 self.__issuer = None 92 self.__clockSkewTolerance = 0. 93 self.__verifyTimeConditions = True 91 94 92 95 def initialise(self, global_conf, prefix='', **app_conf): … … 145 148 value) 146 149 147 148 150 deserialise = property(_getDeserialise, 149 151 _setDeserialise, 150 152 doc="callable to de-serialise response from XML " 151 153 "type") 154 155 def _getIssuer(self): 156 return self.__issuer 157 158 def _setIssuer(self, value): 159 if not isinstance(value, basestring): 160 raise TypeError('Expecting string type for "issuer"; got %r' % 161 type(value)) 162 163 self.__issuer = value 164 165 issuer = property(fget=_getIssuer, 166 fset=_setIssuer, 167 doc="Name of issuing authority") 168 169 def _getVerifyTimeConditions(self): 170 return self.__verifyTimeConditions 171 172 def _setVerifyTimeConditions(self, value): 173 if isinstance(value, bool): 174 self.__verifyTimeConditions = value 175 176 if isinstance(value, basestring): 177 self.__verifyTimeConditions = str2Bool(value) 178 else: 179 raise TypeError('Expecting bool or string type for ' 180 '"verifyTimeConditions"; got %r instead' % 181 type(value)) 182 183 verifyTimeConditions = property(_getVerifyTimeConditions, 184 _setVerifyTimeConditions, 185 doc='Set to True to verify any time ' 186 'Conditions set in the returned ' 187 'response assertions') 188 189 def _getClockSkewTolerance(self): 190 return self.__clockSkewTolerance 191 192 def _setClockSkewTolerance(self, value): 193 if isinstance(value, (float, int, long)): 194 self.__clockSkewTolerance = timedelta(seconds=value) 195 196 elif isinstance(value, basestring): 197 self.__clockSkewTolerance = timedelta(seconds=float(value)) 198 else: 199 raise TypeError('Expecting float, int, long or string type for ' 200 '"clockSkew"; got %r' % type(value)) 201 202 clockSkewTolerance = property(fget=_getClockSkewTolerance, 203 fset=_setClockSkewTolerance, 204 doc="Set a tolerance of +/- n seconds to " 205 "allow for clock skew when checking " 206 "timestamps of client queries") 207 152 208 @classmethod 153 209 def filter_app_factory(cls, app, global_conf, **app_conf): … … 227 283 queryElem = soapRequest.body.elem[0] 228 284 285 # Create a response with basic attributes if provided in the 286 # initialisation config 287 samlResponse = self._initResponse() 288 229 289 try: 230 query = self.deserialise(queryElem) 290 samlQuery = self.deserialise(queryElem) 291 231 292 except UnknownAttrProfile: 232 293 log.exception("%r raised parsing incoming query: " % 233 294 (type(e), traceback.format_exc())) 234 samlResponse = self._makeErrorResponse( 235 StatusCode.UNKNOWN_ATTR_PROFILE_URI) 295 samlResponse.statusCode.value = StatusCode.UNKNOWN_ATTR_PROFILE_URI 236 296 else: 237 297 # Check for Query Interface in environ … … 242 302 self.queryInterfaceKeyName) 243 303 304 # Basic validation 305 self._validateQuery(samlQuery) 306 307 samlResponse.inResponseTo = samlQuery.id 308 244 309 # Call query interface 245 samlResponse = queryInterface(query)310 queryInterface(samlQuery, samlResponse) 246 311 247 312 # Convert to ElementTree representation to enable attachment to SOAP … … 263 328 ('Content-type', 'text/xml')]) 264 329 return [response] 265 266 def _makeErrorResponse(self, code): 267 """Convenience method for making a basic response following an error 330 331 def _validateQuery(self, query): 332 if not self.verifyTimeConditions: 333 log.debug("Skipping verification of SAML Response time conditions") 334 335 utcNow = datetime.utcnow() 336 nowPlusSkew = utcNow + self.clockSkewTolerance 337 338 if response.issueInstant > nowPlusSkew: 339 msg = ('SAML Attribute Query issueInstant [%s] is after ' 340 'the clock time [%s] (skewed +%s)' % 341 (query.issueInstant, 342 SAMLDateTime.toString(nowPlusSkew), 343 self.clockSkewTolerance)) 344 345 samlRespError = QueryIssueInstantInvalid(msg) 346 samlRespError.response = response 347 raise samlRespError 348 349 def _initResponse(self): 350 """Create a SAML Response object with basic settings if any have been 351 provided at initialisation of this class - see initialise 352 353 @return: SAML response object 354 @rtype: ndg.saml.saml2.core.Response 268 355 """ 269 356 samlResponse = Response() 270 271 samlResponse.issueInstant = datetime.utcnow() 357 utcNow = datetime.utcnow() 358 359 samlResponse.issueInstant = utcNow 272 360 samlResponse.id = str(uuid4()) 361 samlResponse.issuer = Issuer() 362 363 # Nb. SAML 2.0 spec says issuer format must be omitted 364 if self.issuer is not None: 365 samlResponse.issuer.value = self.issuer 273 366 274 367 # Initialise to success status but reset on error 275 368 samlResponse.status = Status() 276 369 samlResponse.status.statusCode = StatusCode() 277 samlResponse.status.statusCode.value = code 278 370 samlResponse.status.statusMessage = StatusMessage() 371 samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI 372 373 samlResponse.status.statusMessage = StatusMessage() 374 279 375 return samlResponse 280 376 -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/config/attributeauthority/sitea/site-a.ini
r6615 r6686 27 27 [pipeline:main] 28 28 pipeline = AttributeAuthorityFilter 29 wsseSignatureVerificationFilter30 AttributeAuthorityWsdlSoapBindingFilter31 wsseSignatureFilter32 29 AttributeAuthoritySamlSoapBindingFilter 33 30 mainApp … … 62 59 attributeAuthority.clockSkew: 180.0 63 60 64 # All Attribute Certificates issued are recorded in this dir65 attributeAuthority.attCertDir: %(here)s/attributeCertificateLog66 67 # Files in attCertDir are stored using a rotating file handler68 # attCertFileLogCnt sets the max number of files created before the first is69 # overwritten70 attributeAuthority.attCertFileName: ac.xml71 attributeAuthority.attCertFileLogCnt: 1672 61 attributeAuthority.dnSeparator:/ 73 74 # Location of role mapping file75 attributeAuthority.mapConfigFilePath: %(here)s/siteAMapConfig.xml76 62 77 63 # Settings for custom AttributeInterface derived class to get user roles for given … … 80 66 attributeAuthority.attributeInterface.modName: siteAUserRoles 81 67 attributeAuthority.attributeInterface.className: TestUserRoles 82 83 # Config for XML signature of Attribute Certificate84 attributeAuthority.signingPriKeyFilePath: %(here)s/siteA-aa.key85 attributeAuthority.signingCertFilePath: %(here)s/siteA-aa.crt86 attributeAuthority.caCertFilePathList: $NDGSEC_TEST_CONFIG_DIR/ca/ndg-test-ca.crt87 88 89 # SOAP WSDL Based Binding to the Attribute Authority90 [filter:AttributeAuthorityWsdlSoapBindingFilter]91 paste.filter_app_factory = ndg.security.server.wsgi.attributeauthority:AttributeAuthoritySOAPBindingMiddleware.filter_app_factory92 prefix = service.soap.binding.93 attributeAuthoritySOAPBindingPrefix = attributeauthority.service.soap.binding.94 95 service.soap.binding.referencedFilters = wsseSignatureVerificationFilter0196 service.soap.binding.path = %(attributeAuthoritySoapWsdlServicePath)s97 service.soap.binding.enableWSDLQuery = True98 service.soap.binding.charset = utf-899 service.soap.binding.serviceSOAPBindingEnvironKeyName = ndg.security.server.wsgi.attributeauthority.AttributeAuthoritySOAPBindingMiddleware100 101 attributeauthority.service.soap.binding.attributeAuthorityEnvironKeyName = %(attributeAuthorityEnvironKeyName)s102 attributeauthority.service.soap.binding.wsseSignatureVerificationFilterID = wsseSignatureVerificationFilter01103 104 68 105 69 # SAML SOAP Binding to the Attribute Authority … … 115 79 saml.soapbinding.pathMatchList = /AttributeAuthority/saml 116 80 saml.soapbinding.queryInterfaceKeyName = %(attributeQueryInterfaceEnvironKeyName)s 117 118 119 [filter:wsseSignatureVerificationFilter]120 paste.filter_app_factory = ndg.security.server.wsgi.wssecurity:SignatureVerificationFilter.filter_app_factory121 filterID = wsseSignatureVerificationFilter01122 path = %(attributeAuthoritySoapWsdlServicePath)s123 124 # Settings for WS-Security SignatureHandler class used by this filter125 wsseCfgFilePrefix = wssecurity126 127 # Verify against known CAs - Provide a space separated list of file paths128 wssecurity.caCertFilePathList=$NDGSEC_TEST_CONFIG_DIR/ca/ndg-test-ca.crt129 130 [filter:wsseSignatureFilter]131 paste.filter_app_factory = ndg.security.server.wsgi.wssecurity:ApplySignatureFilter.filter_app_factory132 path = %(attributeAuthoritySoapWsdlServicePath)s133 134 # Reference the verification filter in order to be able to apply signature135 # confirmation136 referencedFilters = wsseSignatureVerificationFilter01137 wsseSignatureVerificationFilterID = wsseSignatureVerificationFilter01138 139 # Last filter in chain SOAP handlers writes the response140 writeResponse = True141 142 # Settings for WS-Security SignatureHandler class used by this filter143 wsseCfgFilePrefix = wssecurity144 145 # Certificate associated with private key used to sign a message. The sign146 # method will add this to the BinarySecurityToken element of the WSSE header.147 wssecurity.signingCertFilePath=%(here)s/siteA-aa.crt148 149 # PEM encoded private key file150 wssecurity.signingPriKeyFilePath=%(here)s/siteA-aa.key151 152 # Set the ValueType for the BinarySecurityToken added to the WSSE header for a153 # signed message. See __setReqBinSecTokValType method and binSecTokValType154 # class variable for options - it may be one of X509, X509v3, X509PKIPathv1 or155 # give full namespace to alternative - see156 # ZSI.wstools.Namespaces.OASIS.X509TOKEN157 #158 # binSecTokValType determines whether signingCert or signingCertChain159 # attributes will be used.160 wssecurity.reqBinSecTokValType=X509v3161 162 # Add a timestamp element to an outbound message163 wssecurity.addTimestamp=True164 165 # For WSSE 1.1 - service returns signature confirmation containing signature166 # value sent by client167 wssecurity.applySignatureConfirmation=True168 81 169 82 -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/attributeauthority/test_attributeauthority.cfg
r5681 r6686 8 8 9 9 [DEFAULT] 10 siteBPropFilePath=$NDGSEC_TEST_CONFIG_DIR/attributeauthority/siteb/siteBAttAuthority.cfg 11 12 [setUp] 13 # ! SiteBMapConfig.xml trusted site A aaURI setting must agree with this 14 # setting for test6GetMappedAttCert 15 propFilePath=$NDGSEC_TEST_CONFIG_DIR/attributeauthority/sitea/siteAAttributeAuthority.cfg 16 17 # For https connections only. !Omit ssl* settings if using http! 18 # sslpeercertcn is the expected CommonName of peer cert. Omit if it's the 19 # same as peer hostname. 20 sslPeerCertCN = AttributeAuthority 21 sslCACertFilePathList = $NDGSEC_TEST_CONFIG_DIR/ca/ndg-test-ca.crt 22 23 [test02GetTrustedHostInfo] 24 role = urn:siteA:security:authz:1.0:attr:postgrad 25 26 [test03GetTrustedHostInfoWithNoMatchingRoleFound] 27 # Set an alternative role to test no matching role found exception 28 role = blah 29 30 [test05GetAttCert] 31 issuingClntCertFilePath = $NDGSEC_TEST_CONFIG_DIR/pki/user.crt 32 33 # Setup for use by test08GetMappedAttCert test 34 attCertFilePath = $NDGSEC_AA_UNITTEST_DIR/ac-clnt.xml 35 36 [test06GetAttCertWithUserIdSet] 37 userId = system 38 attCertFilePath = $NDGSEC_AA_UNITTEST_DIR/ac-clnt-test6.xml 39 40 [test07GetMappedAttCert] 41 issuingClntCertFilePath = $NDGSEC_TEST_CONFIG_DIR/pki/user.crt 42 userAttCertFilePath = $NDGSEC_AA_UNITTEST_DIR/ac-clnt.xml 43 mappedAttCertFilePath = $NDGSEC_AA_UNITTEST_DIR/mapped-ac.xml 44 45 [test08GetMappedAttCertStressTest] 46 issuingClntCertFilePath = $NDGSEC_TEST_CONFIG_DIR/pki/user.crt 47 userAttCertFilePathList = $NDGSEC_AA_UNITTEST_DIR/ac-clnt.xml 48 10 prefix='attribute-authority.' 11 attribute-authority.assertionLifetime = 3600 12 attribute-authority.issuerName = /O=My Organisation/OU=Centre/CN=Attribute Authority 13 attribute-authority.attributeInterface.className = ndg.security.server.attributeauthority.AttributeInterface -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/attributeauthority/test_attributeauthority.py
r6615 r6686 19 19 logging.basicConfig(level=logging.DEBUG) 20 20 21 from os.path import expandvars as xpdVars 22 from os.path import join as jnPath 23 mkPath = lambda file:jnPath(os.environ['NDGSEC_AA_UNITTEST_DIR'], file) 21 from warnings import warn 22 from uuid import uuid4 23 from datetime import datetime 24 from os import path 24 25 25 26 from ndg.security.test.unit import BaseTestCase … … 28 29 CaseSensitiveConfigParser) 29 30 from ndg.security.server.attributeauthority import (AttributeAuthority, 30 AttributeAuthorityNoMatchingRoleInTrustedHosts,31 31 SQLAlchemyAttributeInterface, InvalidAttributeFormat) 32 32 33 from ndg.security.common.AttCert import AttCert 34 35 36 class AttributeAuthorityTestCase(BaseTestCase): 37 clntPriKeyPwd = None 38 39 def setUp(self): 40 super(AttributeAuthorityTestCase, self).setUp() 41 42 if 'NDGSEC_INT_DEBUG' in os.environ: 43 import pdb 44 pdb.set_trace() 45 46 if 'NDGSEC_AA_UNITTEST_DIR' not in os.environ: 47 os.environ['NDGSEC_AA_UNITTEST_DIR'] = \ 48 os.path.abspath(os.path.dirname(__file__)) 49 50 self.cfgParser = CaseSensitiveConfigParser() 51 cfgFilePath = mkPath('test_attributeauthority.cfg') 52 self.cfgParser.read(cfgFilePath) 53 54 self.cfg = {} 55 for section in self.cfgParser.sections() + ['DEFAULT']: 56 self.cfg[section] = dict(self.cfgParser.items(section)) 57 58 self.aa = AttributeAuthority.fromPropertyFile( 59 self.cfg['setUp']['propFilePath']) 60 61 _mkSiteBAttributeAuthority = lambda self: \ 62 AttributeAuthority.fromPropertyFile( 63 propFilePath=self.cfg['DEFAULT']['siteBPropFilePath']) 64 65 def test01GetHostInfo(self): 66 """test01GetHostInfo: retrieve info for AA host""" 67 hostInfo = self.aa.hostInfo 68 print("Host Info:\n %s" % hostInfo) 69 70 def test02GetTrustedHostInfo(self): 71 """test02GetTrustedHostInfo: retrieve trusted host info matching a 72 given role""" 73 thisSection = self.cfg['test02GetTrustedHostInfo'] 74 75 trustedHostInfo = self.aa.getTrustedHostInfo(thisSection['role']) 76 for hostname, hostInfo in trustedHostInfo.items(): 77 self.assert_(hostname, "Hostname not set") 78 for k, v in hostInfo.items(): 79 self.assert_(k, "hostInfo value key unset") 80 81 print("Trusted Host Info:\n %s" % trustedHostInfo) 82 83 def test03GetTrustedHostInfoWithNoMatchingRoleFound(self): 84 """test03GetTrustedHostInfoWithNoMatchingRoleFound: test the case 85 where the input role doesn't match any roles in the target AA's map 86 config file""" 87 thisSection = self.cfg[ 88 'test03GetTrustedHostInfoWithNoMatchingRoleFound'] 89 try: 90 trustedHostInfo = self.aa.getTrustedHostInfo(thisSection['role']) 91 self.fail("Expecting NoMatchingRoleInTrustedHosts exception") 92 93 except AttributeAuthorityNoMatchingRoleInTrustedHosts, e: 94 print('PASSED - no match for role "%s": %s' % (thisSection['role'], 95 e)) 96 97 98 def test04GetTrustedHostInfoWithNoRole(self): 99 """test04GetTrustedHostInfoWithNoRole: retrieve trusted host info 100 irrespective of role""" 101 trustedHostInfo = self.aa.getTrustedHostInfo() 102 for hostname, hostInfo in trustedHostInfo.items(): 103 self.assert_(hostname, "Hostname not set") 104 for k, v in hostInfo.items(): 105 self.assert_(k, "hostInfo value key unset") 106 107 print("Trusted Host Info:\n %s" % trustedHostInfo) 108 109 def test05GetAttCert(self): 110 """test05GetAttCert: Request attribute certificate from NDG Attribute 111 Authority Web Service.""" 112 thisSection = self.cfg['test05GetAttCert'] 113 114 # Read user Certificate into a string ready for passing via WS 115 try: 116 userX509CertFilePath = xpdVars(thisSection.get( 117 'issuingClntCertFilePath')) 118 userX509CertTxt = open(userX509CertFilePath, 'r').read() 119 120 except TypeError: 121 # No issuing cert set 122 userX509CertTxt = None 123 124 except IOError, ioErr: 125 raise Exception("Error reading certificate file \"%s\": %s" % 126 (ioErr.filename, ioErr.strerror)) 127 128 # Make attribute certificate request 129 attCert = self.aa.getAttCert(holderX509Cert=userX509CertTxt) 130 131 print("Attribute Certificate: \n\n:" + str(attCert)) 132 133 attCert.filePath = xpdVars(thisSection['attCertFilePath']) 134 attCert.write() 135 136 137 def test06GetAttCertWithUserIdSet(self): 138 """test06GetAttCertWithUserIdSet: Request attribute certificate from 139 NDG Attribute Authority Web Service setting a specific user Id 140 independent of the signer of the SOAP request.""" 141 thisSection = self.cfg['test06GetAttCertWithUserIdSet'] 142 143 # Make attribute certificate request 144 userId = thisSection['userId'] 145 attCert = self.aa.getAttCert(userId=userId) 146 147 print("Attribute Certificate: \n\n:" + str(attCert)) 148 149 attCert.filePath = xpdVars(thisSection['attCertFilePath']) 150 attCert.write() 151 152 153 def test07GetMappedAttCert(self): 154 """test07GetMappedAttCert: Request mapped attribute certificate from 155 NDG Attribute Authority Web Service.""" 156 thisSection = self.cfg['test07GetMappedAttCert'] 157 158 # Read user Certificate into a string ready for passing via WS 159 try: 160 userX509CertFilePath = xpdVars(thisSection.get( 161 'issuingClntCertFilePath')) 162 userX509CertTxt = open(userX509CertFilePath, 'r').read() 163 164 except TypeError: 165 # No issuing cert set 166 userX509CertTxt = None 167 168 except IOError, ioErr: 169 raise Exception("Error reading certificate file \"%s\": %s" % 170 (ioErr.filename, ioErr.strerror)) 171 172 # Simlarly for Attribute Certificate 173 try: 174 userAttCert = AttCert.Read( 175 xpdVars(thisSection['userAttCertFilePath'])) 176 177 except IOError, ioErr: 178 raise Exception("Error reading attribute certificate file \"%s\": " 179 "%s" % (ioErr.filename, ioErr.strerror)) 180 181 # Make client to site B Attribute Authority 182 siteBAA = self._mkSiteBAttributeAuthority() 183 184 # Make attribute certificate request 185 attCert = siteBAA.getAttCert(holderX509Cert=userX509CertTxt, 186 userAttCert=userAttCert) 187 print("Attribute Certificate: \n\n:" + str(attCert)) 188 189 attCert.filePath = xpdVars(thisSection['mappedAttCertFilePath']) 190 attCert.write() 191 192 193 def test08GetMappedAttCertStressTest(self): 194 """test08GetMappedAttCertStressTest: Request mapped attribute 195 certificate from NDG Attribute Authority Web Service.""" 196 thisSection = self.cfg['test08GetMappedAttCertStressTest'] 197 198 # Read user Certificate into a string ready for passing via WS 199 try: 200 userX509CertFilePath = xpdVars(thisSection.get( 201 'issuingClntCertFilePath')) 202 userX509CertTxt = open(userX509CertFilePath, 'r').read() 203 204 except TypeError: 205 # No issuing cert set 206 userX509CertTxt = None 207 208 except IOError, ioErr: 209 raise Exception("Error reading certificate file \"%s\": %s" % 210 (ioErr.filename, ioErr.strerror)) 211 212 # Make client to site B Attribute Authority 213 siteBAA = self._mkSiteBAttributeAuthority() 214 215 acFilePathList = [xpdVars(file) for file in \ 216 thisSection['userAttCertFilePathList'].split()] 217 218 passed = True 219 for acFilePath in acFilePathList: 220 try: 221 userAttCert = AttCert.Read(acFilePath) 222 223 except IOError, ioErr: 224 raise Exception("Error reading attribute certificate file " 225 '"%s": %s' % (ioErr.filename, ioErr.strerror)) 226 227 # Make attribute certificate request 228 try: 229 attCert = siteBAA.getAttCert(holderX509Cert=userX509CertTxt, 230 userAttCert=userAttCert) 231 except Exception, e: 232 passed = True 233 outFilePfx = 'test08GetMappedAttCertStressTest-%s' % \ 234 os.path.basename(acFilePath) 235 msgFile = open(outFilePfx+".msg", 'w') 236 msgFile.write('Failed for "%s": %s\n' % (acFilePath, e)) 237 238 self.assert_(passed, 239 "At least one Attribute Certificate request failed. " 240 "Check the .msg files in this directory") 241 242 243 from warnings import warn 244 from uuid import uuid4 245 from datetime import datetime 246 from ndg.saml.saml2.core import (Response, Attribute, SAMLVersion, Subject, NameID, 247 Issuer, AttributeQuery, XSStringAttributeValue, 248 Status, StatusMessage, StatusCode) 33 from ndg.saml.saml2.core import (Response, Attribute, SAMLVersion, Subject, 34 NameID, Issuer, AttributeQuery, 35 XSStringAttributeValue, Status, StatusMessage, 36 StatusCode) 249 37 from ndg.saml.xml import XMLConstants 250 38 from ndg.security.common.saml_utils.esg import EsgSamlNamespaces 251 39 40 THIS_DIR = path.dirname(__file__) 41 42 43 class AttributeAuthorityTestCase(BaseTestCase): 44 THIS_DIR = THIS_DIR 45 PROPERTIES_FILENAME = 'test_attributeauthority.cfg' 46 PROPERTIES_FILEPATH = path.join(THIS_DIR, PROPERTIES_FILENAME) 47 ISSUER_NAME = '/O=My Organisation/OU=Centre/CN=Attribute Authority' 48 49 def test01ParsePropertiesFile(self): 50 cls = AttributeAuthorityTestCase 51 aa = AttributeAuthority.fromPropertyFile(cls.PROPERTIES_FILEPATH) 52 self.assert_(aa) 53 self.assert_(aa.assertionLifetime == 3600) 54 self.assert_(aa.issuerName == cls.ISSUER_NAME) 55 56 def test02FromPropertis(self): 57 58 # Casts from string to float 59 assertionLifetime = "86400" 60 issuerName = 'My issuer' 61 aa = AttributeAuthority.fromProperties(issuerName=issuerName, 62 assertionLifetime=assertionLifetime) 63 self.assert_(aa) 64 self.assert_(aa.assertionLifetime == float(assertionLifetime)) 65 self.assert_(aa.issuerName == issuerName) 66 252 67 253 68 class SQLAlchemyAttributeInterfaceTestCase(BaseTestCase): 69 THIS_DIR = THIS_DIR 70 PROPERTIES_FILENAME = 'test_sqlalchemyattributeinterface.cfg' 71 PROPERTIES_FILEPATH = path.join(THIS_DIR, PROPERTIES_FILENAME) 72 254 73 SAML_SUBJECT_SQLQUERY = ("select count(*) from users where openid = " 255 74 "'${userId}'") … … 364 183 return 365 184 cfgParser = CaseSensitiveConfigParser() 366 cfgFilePath = mkPath('test_sqlalchemyattributeinterface.cfg') 185 cls = SQLAlchemyAttributeInterfaceTestCase 186 cfgFilePath = cls.PROPERTIES_FILEPATH 367 187 cfgParser.read(cfgFilePath) 368 188 -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/saml/test_samlinterface.py
r6615 r6686 20 20 from xml.etree import ElementTree 21 21 22 from ndg.saml.utils import SAMLDateTime 22 23 from ndg.saml.saml2.core import (Response, Assertion, Attribute, 23 24 AttributeStatement, SAMLVersion, Subject, NameID, … … 31 32 from ndg.security.common.soap.etree import SOAPEnvelope 32 33 from ndg.security.common.utils.etree import QName, prettyPrint 34 from ndg.security.common.saml_utils.binding.soap.subjectquery import ( 35 SubjectQuerySOAPBinding, ResponseIssueInstantInvalid, 36 AssertionIssueInstantInvalid, AssertionConditionNotBeforeInvalid, 37 AssertionConditionNotOnOrAfterInvalid) 38 33 39 from ndg.security.common.saml_utils.esg import (EsgSamlNamespaces, 34 40 XSGroupRoleAttributeValue) … … 160 166 """TODO: test SAML Attribute Authority interface""" 161 167 thisDir = os.path.dirname(os.path.abspath(__file__)) 168 RESPONSE = '''\ 169 <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> 170 <SOAP-ENV:Body> 171 <samlp:Response ID="05680cb2-4973-443d-9d31-7bc99bea87c1" InResponseTo="e3183380-ae82-4285-8827-8c40613842de" IssueInstant="%(issueInstant)s" Version="2.0" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"> 172 <saml:Issuer Format="urn:esg:issuer" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">ESG-NCAR</saml:Issuer> 173 <samlp:Status> 174 <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> 175 </samlp:Status> 176 <saml:Assertion ID="192c67d9-f9cd-457a-9242-999e7b943166" IssueInstant="%(assertionIssueInstant)s" Version="2.0" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"> 177 <saml:Issuer Format="urn:esg:issuer">ESG-NCAR</saml:Issuer> 178 <saml:Subject> 179 <saml:NameID Format="urn:esg:openid">https://esg.prototype.ucar.edu/myopenid/testUser</saml:NameID> 180 </saml:Subject> 181 <saml:Conditions NotBefore="%(notBefore)s" NotOnOrAfter="%(notOnOrAfter)s" /> 182 <saml:AttributeStatement> 183 <saml:Attribute FriendlyName="FirstName" Name="urn:esg:first:name" NameFormat="http://www.w3.org/2001/XMLSchema#string"> 184 <saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Test</saml:AttributeValue> 185 </saml:Attribute> 186 <saml:Attribute FriendlyName="LastName" Name="urn:esg:last:name" NameFormat="http://www.w3.org/2001/XMLSchema#string"> 187 <saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">User</saml:AttributeValue> 188 </saml:Attribute> 189 <saml:Attribute FriendlyName="EmailAddress" Name="urn:esg:first:email:address" NameFormat="http://www.w3.org/2001/XMLSchema#string"> 190 <saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">ejn@ucar.edu</saml:AttributeValue> 191 </saml:Attribute> 192 <saml:Attribute FriendlyName="GroupRole" Name="urn:esg:group:role" NameFormat="groupRole"> 193 <saml:AttributeValue> 194 <esg:groupRole group="CCSM" role="default" xmlns:esg="http://www.esg.org" /> 195 </saml:AttributeValue> 196 <saml:AttributeValue> 197 <esg:groupRole group="Dynamical Core" role="default" xmlns:esg="http://www.esg.org" /> 198 </saml:AttributeValue> 199 <saml:AttributeValue> 200 <esg:groupRole group="NARCCAP" role="default" xmlns:esg="http://www.esg.org" /> 201 </saml:AttributeValue> 202 </saml:Attribute> 203 </saml:AttributeStatement> 204 </saml:Assertion> 205 </samlp:Response> 206 </SOAP-ENV:Body> 207 </SOAP-ENV:Envelope> 208 ''' 162 209 163 210 def __init__(self, *args, **kwargs): … … 167 214 BaseTestCase.__init__(self, *args, **kwargs) 168 215 169 170 216 def test01AttributeQuery(self): 171 217 attributeQuery = AttributeQuery() … … 389 435 self.assert_(response) 390 436 437 def _parseResponse(self, responseStr): 438 """Helper to parse a response from a string""" 439 soapResponse = SOAPEnvelope() 440 441 responseStream = StringIO() 442 responseStream.write(responseStr) 443 responseStream.seek(0) 444 445 soapResponse.parse(responseStream) 446 447 print("Parsed response ...") 448 print(soapResponse.serialize()) 449 450 toSAMLTypeMap = [XSGroupRoleAttributeValueElementTree.factoryMatchFunc] 451 response = ResponseElementTree.fromXML(soapResponse.body.elem[0], 452 customToSAMLTypeMap=toSAMLTypeMap) 453 return response 454 455 def test03ParseResponse(self): 456 utcNow = datetime.utcnow() 457 respDict = { 458 'issueInstant': SAMLDateTime.toString(utcNow), 459 'assertionIssueInstant': SAMLDateTime.toString(utcNow), 460 'notBefore': SAMLDateTime.toString(utcNow), 461 'notOnOrAfter': SAMLDateTime.toString(utcNow + timedelta( 462 seconds=60*60*8)) 463 } 464 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 465 respDict 466 response = self._parseResponse(responseStr) 467 self.assert_(response) 468 469 def test04AssertionConditionExpired(self): 470 # issued 9 hours ago 471 issueInstant = datetime.utcnow() - timedelta(seconds=60*60*9) 472 respDict = { 473 'issueInstant': SAMLDateTime.toString(issueInstant), 474 'assertionIssueInstant': SAMLDateTime.toString(issueInstant), 475 'notBefore': SAMLDateTime.toString(issueInstant), 476 # It lasts for 8 hours so it's expired by one hour 477 'notOnOrAfter': SAMLDateTime.toString(issueInstant + timedelta( 478 seconds=60*60*8)) 479 } 480 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 481 respDict 482 response = self._parseResponse(responseStr) 483 binding = SubjectQuerySOAPBinding() 484 try: 485 binding._verifyTimeConditions(response) 486 self.fail("Expecting not on or after timestamp error") 487 except AssertionConditionNotOnOrAfterInvalid, e: 488 print("PASSED: %s" % e) 489 490 def test05ResponseIssueInstantInvalid(self): 491 utcNow = datetime.utcnow() 492 respDict = { 493 'issueInstant': SAMLDateTime.toString(utcNow + timedelta( 494 seconds=1)), 495 'assertionIssueInstant': SAMLDateTime.toString(utcNow), 496 'notBefore': SAMLDateTime.toString(utcNow), 497 'notOnOrAfter': SAMLDateTime.toString(utcNow + timedelta( 498 seconds=60*60*8)) 499 } 500 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 501 respDict 502 response = self._parseResponse(responseStr) 503 binding = SubjectQuerySOAPBinding() 504 try: 505 binding._verifyTimeConditions(response) 506 self.fail("Expecting issue instant timestamp error") 507 except ResponseIssueInstantInvalid, e: 508 print("PASSED: %s" % e) 509 510 def test06NotBeforeConditionInvalid(self): 511 utcNow = datetime.utcnow() 512 respDict = { 513 'issueInstant': SAMLDateTime.toString(utcNow), 514 'assertionIssueInstant': SAMLDateTime.toString(utcNow), 515 'notBefore': SAMLDateTime.toString(utcNow + timedelta(seconds=1)), 516 'notOnOrAfter': SAMLDateTime.toString(utcNow + timedelta( 517 seconds=60*60*8)) 518 } 519 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 520 respDict 521 response = self._parseResponse(responseStr) 522 binding = SubjectQuerySOAPBinding() 523 try: 524 binding._verifyTimeConditions(response) 525 self.fail("Expecting issue instant timestamp error") 526 except AssertionConditionNotBeforeInvalid, e: 527 print("PASSED: %s" % e) 528 529 def test07AssertionIssueInstantInvalid(self): 530 utcNow = datetime.utcnow() 531 respDict = { 532 'issueInstant': SAMLDateTime.toString(utcNow), 533 'assertionIssueInstant': SAMLDateTime.toString(utcNow + timedelta( 534 seconds=1)), 535 'notBefore': SAMLDateTime.toString(utcNow), 536 'notOnOrAfter': SAMLDateTime.toString(utcNow + timedelta( 537 seconds=60*60*8)) 538 } 539 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 540 respDict 541 response = self._parseResponse(responseStr) 542 binding = SubjectQuerySOAPBinding() 543 try: 544 binding._verifyTimeConditions(response) 545 self.fail("Expecting issue instant timestamp error") 546 except AssertionIssueInstantInvalid, e: 547 print("PASSED: %s" % e) 548 549 def test07ClockSkewCorrectedAssertionIssueInstantInvalid(self): 550 utcNow = datetime.utcnow() 551 respDict = { 552 'issueInstant': SAMLDateTime.toString(utcNow), 553 'assertionIssueInstant': SAMLDateTime.toString(utcNow + timedelta( 554 seconds=1)), 555 'notBefore': SAMLDateTime.toString(utcNow), 556 'notOnOrAfter': SAMLDateTime.toString(utcNow + timedelta( 557 seconds=60*60*8)) 558 } 559 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 560 respDict 561 response = self._parseResponse(responseStr) 562 binding = SubjectQuerySOAPBinding() 563 564 # Set a skew to correct the error 565 binding.clockSkewTolerance = 1 566 567 try: 568 binding._verifyTimeConditions(response) 569 except AssertionIssueInstantInvalid, e: 570 self.fail("issue instant timestamp error should be corrected for") 571 572 def test08ClockSkewCorrectedAssertionConditionExpired(self): 573 # Issued 9 hours ago 574 issueInstant = datetime.utcnow() - timedelta(seconds=60*60*9) 575 respDict = { 576 'issueInstant': SAMLDateTime.toString(issueInstant), 577 'assertionIssueInstant': SAMLDateTime.toString(issueInstant), 578 'notBefore': SAMLDateTime.toString(issueInstant), 579 # Assertion lasts 8 hours so it has expired by one hour 580 'notOnOrAfter': SAMLDateTime.toString(issueInstant + timedelta( 581 seconds=60*60*8)) 582 } 583 responseStr = SamlAttributeAuthorityInterfaceTestCase.RESPONSE % \ 584 respDict 585 response = self._parseResponse(responseStr) 586 binding = SubjectQuerySOAPBinding() 587 588 # Set a skew of over one hour to correct for the assertion expiry 589 binding.clockSkewTolerance = 60*60 + 3 590 591 try: 592 binding._verifyTimeConditions(response) 593 594 except AssertionConditionNotOnOrAfterInvalid, e: 595 self.fail("Not on or after timestamp error should be corrected for") 596 391 597 392 598 if __name__ == "__main__":
Note: See TracChangeset
for help on using the changeset viewer.