Home | Trees | Indices | Help |
|
---|
|
1 """SAML 2.0 bindings module implements SOAP binding for base request 2 3 NERC DataGrid Project 4 """ 5 __author__ = "P J Kershaw" 6 __date__ = "12/02/10" 7 __copyright__ = "(C) 2010 Science and Technology Facilities Council" 8 __license__ = "http://www.apache.org/licenses/LICENSE-2.0" 9 __contact__ = "Philip.Kershaw@stfc.ac.uk" 10 __revision__ = '$Id: subjectquery.py 7634 2010-10-20 20:23:29Z pjkersha $' 11 import logging 12 log = logging.getLogger(__name__) 13 14 import copy 15 from datetime import datetime, timedelta 16 from uuid import uuid4 17 18 from ndg.saml.utils import SAMLDateTime 19 from ndg.saml.saml2.core import (RequestAbstractType, StatusCode, Issuer, 20 SAMLVersion) 21 22 from ndg.saml.utils import str2Bool 23 from ndg.saml.saml2.binding.soap.client import (SOAPBinding, 24 SOAPBindingInvalidResponse) 25 26 29 30 33 34 37 38 41 42 46 47 51 52 54 """SAML Request Base SOAP Binding 55 """ 56 ISSUER_NAME_OPTNAME = 'issuerName' 57 ISSUER_FORMAT_OPTNAME = 'issuerFormat' 58 CLOCK_SKEW_OPTNAME = 'clockSkewTolerance' 59 VERIFY_TIME_CONDITIONS_OPTNAME = 'verifyTimeConditions' 60 61 CONFIG_FILE_OPTNAMES = ( 62 ISSUER_NAME_OPTNAME, 63 ISSUER_FORMAT_OPTNAME, 64 CLOCK_SKEW_OPTNAME, 65 VERIFY_TIME_CONDITIONS_OPTNAME 66 ) 67 68 __PRIVATE_ATTR_PREFIX = "__" 69 __slots__ = tuple([__PRIVATE_ATTR_PREFIX + i 70 for i in CONFIG_FILE_OPTNAMES + ('issuer',)]) 71 del i 72 73 QUERY_TYPE = RequestAbstractType 7476 '''Create SOAP Client for a SAML Subject Query''' 77 self.__clockSkewTolerance = timedelta(seconds=0.) 78 self.__verifyTimeConditions = True 79 self.__issuer = Issuer() 80 self.__issuer.format = Issuer.X509_SUBJECT 81 82 super(RequestBaseSOAPBinding, self).__init__(**kw)8385 query = self.__class__.QUERY_TYPE() 86 query.version = SAMLVersion(SAMLVersion.VERSION_20) 87 self.addQueryAttributes(query) 88 return query89 9294 return self.__issuer95 98 99 issuer = property(_getIssuer, _setIssuer, 100 doc="Issuer") 101 107 113 114 issuerFormat = property(_getIssuerFormat, _setIssuerFormat, 115 doc="Issuer format") 116 122 128 129 issuerName = property(_getIssuerName, _setIssuerName, 130 doc="Name of issuer of SAML Subject Query") 131133 return self.__verifyTimeConditions134136 if isinstance(value, bool): 137 self.__verifyTimeConditions = value 138 139 if isinstance(value, basestring): 140 self.__verifyTimeConditions = str2Bool(value) 141 else: 142 raise TypeError('Expecting bool or string type for ' 143 '"verifyTimeConditions"; got %r instead' % 144 type(value))145 146 verifyTimeConditions = property(_getVerifyTimeConditions, 147 _setVerifyTimeConditions, 148 doc='Set to True to verify any time ' 149 'Conditions set in the returned ' 150 'response assertions') 151153 return self.__clockSkewTolerance154156 if isinstance(value, timedelta): 157 self.__clockSkewTolerance = value 158 159 elif isinstance(value, (float, int, long)): 160 self.__clockSkewTolerance = timedelta(seconds=value) 161 162 elif isinstance(value, basestring): 163 self.__clockSkewTolerance = timedelta(seconds=float(value)) 164 else: 165 raise TypeError('Expecting timedelta, float, int, long or string ' 166 'type for "clockSkewTolerance"; got %r' % 167 type(value))168 169 clockSkewTolerance = property(fget=_getClockSkewTolerance, 170 fset=_setClockSkewTolerance, 171 doc="Allow a tolerance in seconds for SAML " 172 "Query issueInstant parameter check and " 173 "assertion condition notBefore and " 174 "notOnOrAfter times to allow for clock " 175 "skew") 176178 """Perform sanity check immediately before creating the query and 179 sending it""" 180 errors = [] 181 182 if query.issuer.value is None: 183 errors.append('issuer name') 184 185 if query.issuer.format is None: 186 errors.append('issuer format') 187 188 if errors: 189 raise AttributeError('Missing attribute(s) for SAML Query: %s' % 190 ', '.join(errors))191193 """Perform any final initialisation prior to sending the query - derived 194 classes may overload to specify as required""" 195 query.issueInstant = datetime.utcnow() 196 197 # Set ID here to ensure it's unique for each new call 198 query.id = str(uuid4())199201 """Verify time conditions set in a response 202 @param response: SAML Response returned from remote service 203 @type response: ndg.saml.saml2.core.Response 204 @raise RequestResponseError: if a timestamp is invalid 205 """ 206 207 if not self.verifyTimeConditions: 208 log.debug("Skipping verification of SAML Response time conditions") 209 210 utcNow = datetime.utcnow() 211 nowMinusSkew = utcNow - self.clockSkewTolerance 212 nowPlusSkew = utcNow + self.clockSkewTolerance 213 214 if response.issueInstant > nowPlusSkew: 215 msg = ('SAML Attribute Response issueInstant [%s] is after ' 216 'the clock time [%s] (skewed +%s)' % 217 (response.issueInstant, 218 SAMLDateTime.toString(nowPlusSkew), 219 self.clockSkewTolerance)) 220 221 samlRespError = ResponseIssueInstantInvalid(msg) 222 samlRespError.response = response 223 raise samlRespError 224 225 for assertion in response.assertions: 226 if assertion.issueInstant is None: 227 samlRespError = AssertionIssueInstantInvalid("No issueInstant " 228 "set in response " 229 "assertion") 230 samlRespError.response = response 231 raise samlRespError 232 233 elif nowPlusSkew < assertion.issueInstant: 234 msg = ('The clock time [%s] (skewed +%s) is before the ' 235 'SAML Attribute Response assertion issue instant [%s]' % 236 (SAMLDateTime.toString(utcNow), 237 self.clockSkewTolerance, 238 assertion.issueInstant)) 239 samlRespError = AssertionIssueInstantInvalid(msg) 240 samlRespError.response = response 241 raise samlRespError 242 243 if assertion.conditions is not None: 244 if nowPlusSkew < assertion.conditions.notBefore: 245 msg = ('The clock time [%s] (skewed +%s) is before the ' 246 'SAML Attribute Response assertion conditions not ' 247 'before time [%s]' % 248 (SAMLDateTime.toString(utcNow), 249 self.clockSkewTolerance, 250 assertion.conditions.notBefore)) 251 252 samlRespError = AssertionConditionNotBeforeInvalid(msg) 253 samlRespError.response = response 254 raise samlRespError 255 256 if nowMinusSkew >= assertion.conditions.notOnOrAfter: 257 msg = ('The clock time [%s] (skewed -%s) is on or after ' 258 'the SAML Attribute Response assertion conditions ' 259 'not on or after time [%s]' % 260 (SAMLDateTime.toString(utcNow), 261 self.clockSkewTolerance, 262 assertion.conditions.notOnOrAfter)) 263 264 samlRespError = AssertionConditionNotOnOrAfterInvalid(msg) 265 samlRespError.response = response 266 raise samlRespError267269 '''Make an attribute query to a remote SAML service 270 271 @type uri: basestring 272 @param uri: uri of service. May be omitted if set from request.url 273 @type request: ndg.security.common.soap.UrlLib2SOAPRequest 274 @param request: SOAP request object to which query will be attached 275 defaults to ndg.security.common.soap.client.UrlLib2SOAPRequest 276 ''' 277 self._validateQueryParameters(query) 278 self._initSend(query) 279 280 log.debug("Sending request: query ID: %s", query.id) 281 response = super(RequestBaseSOAPBinding, self).send(query, **kw) 282 283 # Perform validation 284 if response.status.statusCode.value != StatusCode.SUCCESS_URI: 285 msg = ('Return status code flagged an error, %r. ' 286 'The message is, %r' % 287 (response.status.statusCode.value, 288 response.status.statusMessage.value)) 289 samlRespError = RequestResponseError(msg) 290 samlRespError.response = response 291 raise samlRespError 292 293 # Check Query ID matches the query ID the service received 294 if response.inResponseTo != query.id: 295 msg = ('Response in-response-to ID %r, doesn\'t match the original ' 296 'query ID, %r' % (response.inResponseTo, query.id)) 297 298 samlRespError = RequestResponseError(msg) 299 samlRespError.response = response 300 raise samlRespError 301 302 self._verifyTimeConditions(response) 303 304 return response305
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Apr 4 22:19:49 2012 | http://epydoc.sourceforge.net |