Changeset 7257
- Timestamp:
- 29/07/10 21:32:16 (11 years ago)
- Location:
- TI12-security/trunk/NDGSecurity/python
- Files:
-
- 1 added
- 3 deleted
- 4 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/pep.py
r7168 r7257 1 '''NDG Security Policy Enforcement Point Module 2 3 __author__ = "P J Kershaw" 4 __date__ = "11/07/10" 5 __copyright__ = "(C) 2010 Science and Technology Facilities Council" 6 __license__ = "BSD - see LICENSE file in top-level directory" 7 __contact__ = "Philip.Kershaw@stfc.ac.uk" 8 __revision__ = '$Id:$' 1 9 ''' 2 Created on 11 Jul 2010 3 4 @author: pjkersha 5 ''' 6 from webob import Request 7 10 import logging 11 log = logging.getLogger(__name__) 12 13 import httplib 14 from time import time 15 16 import webob 17 18 from ndg.saml.saml2.core import DecisionType 8 19 from ndg.saml.saml2.binding.soap.client.authzdecisionquery import \ 9 20 AuthzDecisionQuerySslSOAPBinding 10 from ndg.security.server.wsgi.session import SessionHandlerMiddleware 21 from ndg.security.server.wsgi.session import (SessionMiddlewareBase, 22 SessionHandlerMiddleware) 11 23 12 24 … … 15 27 16 28 17 class SamlPepMiddleware( object):29 class SamlPepMiddleware(SessionMiddlewareBase): 18 30 '''Policy Enforcement Point for ESG with SAML based Interface 19 31 … … 165 177 self.session = environ[self.sessionKey] 166 178 167 request = Request(environ) 168 self.__client.resourceURI = request.url 169 self.__client.subjectID = request.remote_user or '' 170 171 self.__client.send(uri=self.__authzServiceURI) 172 179 request = webob.Request(environ) 180 self.client.resourceURI = request.url 181 182 # Nb. user may not be logged in hence REMOTE_USER is not set 183 self.client.subjectID = request.remote_user or '' 184 185 samlAuthzResponse = self.client.send(uri=self.__authzServiceURI) 186 187 # Record the result in the user's session to enable later 188 # interrogation by any result handler Middleware 189 self.setSession(self.client.query, samlAuthzResponse) 190 191 # Set HTTP 403 Forbidden response if any of the decisions returned are 192 # deny or indeterminate status 193 failDecisions = (DecisionType.DENY, DecisionType.INDETERMINATE) 194 195 for assertion in samlAuthzResponse.assertions: 196 for authzDecisionStatement in assertion.authzDecisionStatements: 197 if authzDecisionStatement.decision.value in failDecisions: 198 response = webob.Response() 199 200 if not self.client.subjectID: 201 # Access failed and the user is not logged in 202 response.status = httplib.UNAUTHORIZED 203 else: 204 # The user is logged in but not authorised 205 response.status = httplib.FORBIDDEN 206 207 response.body = 'Access denied to %r for user %r' % ( 208 self.client.resourceURI, 209 self.client.subjectID) 210 response.content_type = 'text/plain' 211 log.info(response.body) 212 return response(environ, start_response) 213 214 # If got through to here then all is well, call next WSGI middleware/app 173 215 return self._app(environ, start_response) 174 216 175 def _createAuthzDecisionQuery(self, 176 resourceURI, 177 subject): 178 """Create SAML authorisation decision query object ready for dispatch 217 def setSession(self, request, response, save=True): 218 """Set PEP context information in the Beaker session using standard key 219 names 220 221 @param session: beaker session 222 @type session: beaker.session.SessionObject 223 @param request: authorisation decision query 224 @type request: ndg.saml.saml2.core.AuthzDecisionQuery 225 @param response: authorisation response 226 @type response: ndg.saml.saml2.core.Response 227 @param save: determines whether session is saved or not 228 @type save: bool 179 229 """ 180 # query = AuthzDecisionQuery() 181 # query.version = SAMLVersion(SAMLVersion.VERSION_20) 182 # query.id = str(uuid4()) 183 # query.issueInstant = datetime.utcnow() 184 # 185 # query.issuer = Issuer() 186 # query.issuer.format = Issuer.X509_SUBJECT 187 # query.issuer.value = issuer 188 # 189 # query.subject = Subject() 190 # query.subject.nameID = NameID() 191 # query.subject.nameID.format = "urn:ndg:saml:test:openid" 192 # query.subject.nameID.value = subject 193 # 194 # query.resource = resource 195 # 196 # query.actions.append(Action()) 197 # query.actions[0].namespace = actionNs 198 # query.actions[0].value = action 199 # 200 # return query 230 self.session[self.__class__.PEPCTX_SESSION_KEYNAME] = { 231 self.__class__.PEPCTX_REQUEST_SESSION_KEYNAME: request, 232 self.__class__.PEPCTX_RESPONSE_SESSION_KEYNAME: response, 233 self.__class__.PEPCTX_TIMESTAMP_SESSION_KEYNAME: time() 234 } 235 236 if save: 237 self.session.save() -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/service.py
r7168 r7257 12 12 log = logging.getLogger(__name__) 13 13 14 from datetime import datetime, timedelta 15 from uuid import uuid4 16 17 # SAML authorisation decision query interface 18 from ndg.saml.saml2.core import (Response, AuthzDecisionStatement, Assertion, 19 Action, DecisionType, SAMLVersion, Issuer, 20 Status, StatusCode, StatusMessage, NameID, 21 Subject, Conditions) 14 from ndg.xacml.core.context.pdp import PDP 15 from ndg.xacml.parsers.etree.factory import ReaderFactory as \ 16 XacmlEtreePolicyReaderFactory 22 17 23 18 from ndg.security.common.authz.pip.esginterface import PIP 24 25 # XACML Policy Decision Point 26 from ndg.xacml.parsers.etree.factory import ReaderFactory 27 from ndg.xacml.core.attribute import Attribute 28 from ndg.xacml.core.attributevalue import AttributeValueClassFactory 29 from ndg.xacml.core.context.pdpinterface import PDPInterface 30 from ndg.xacml.core.context.pdp import PDP 31 from ndg.xacml.core.context.request import Request 32 from ndg.xacml.core.context.resource import Resource 33 from ndg.xacml.core import context 34 35 36 class AuthzServiceMiddlewareError(Exception): 19 from ndg.security.server.xacml.ctx_handler import saml_ctx_handler 20 21 22 class AuthorisationServiceMiddlewareError(Exception): 37 23 """Authorisation Service generic exception type""" 38 24 39 25 40 class AuthzServiceMiddlewareConfigError(AuthzServiceMiddlewareError): 26 class AuthorisationServiceMiddlewareConfigError( 27 AuthorisationServiceMiddlewareError): 41 28 """Authorisation Service configuration error""" 42 29 43 30 44 class Auth zServiceMiddleware(object):31 class AuthorisationServiceMiddleware(object): 45 32 '''WSGI to add an NDG Security Authorization Service in the environ. 33 34 @cvar PIP_CFG_PREFIX: prefix for Policy Information Point related parameters 35 @type PIP_CFG_PREFIX: string 46 36 ''' 37 DEFAULT_PARAM_PREFIX = 'authorisationService.' 47 38 DEFAULT_QUERY_IFACE_KEYNAME = \ 48 39 'ndg.security.server.wsgi.authzservice.queryInterface' 49 40 50 41 ENVIRON_KEYNAME_QUERY_IFACE_OPTNAME = 'queryInterfaceKeyName' 51 ASSERTION_LIFETIME_OPTNAME = 'assertionLifetime' 42 43 XACML_CTX_HANDLER_PARAM_PREFIX = 'xacmlContext.' 52 44 53 45 # For loop based assignment where possible of config options in initialise() 54 46 AUTHZ_SRVC_OPTION_DEFAULTS = { 55 47 ENVIRON_KEYNAME_QUERY_IFACE_OPTNAME: DEFAULT_QUERY_IFACE_KEYNAME, 56 ASSERTION_LIFETIME_OPTNAME: 60*60*8, # 8 hours as default57 48 } 58 49 … … 61 52 62 53 __slots__ = ( 63 '__ policyFilePath',54 '__xacmlCtxHandler', 64 55 '__queryInterface', 65 56 '__' + ENVIRON_KEYNAME_QUERY_IFACE_OPTNAME, 66 '__' + ASSERTION_LIFETIME_OPTNAME,67 '__pdp',68 57 '_app', 69 58 ) 70 59 71 60 def __init__(self, app): 72 '''Set-up an Authorization Service instance 61 '''Set-up an Authorisation Service instance 62 63 @param app: next app/middleware in WSGI stack 64 @type app: callable 73 65 ''' 74 66 self._app = app 75 self.__policyFilePath = None 76 self.__pdp = None 67 self.__xacmlCtxHandler = saml_ctx_handler.SamlCtxHandler() 77 68 self.__queryInterface = None 78 69 self.__queryInterfaceKeyName = None 79 self.__assertionLifetime = 0. 80 81 def initialise(self, global_conf, prefix='authorizationservice.', 82 **app_conf): 83 """Set-up Authorization Service middleware using a Paste app factory 84 pattern. Overloaded base class method to enable custom settings from 85 app_conf 86 87 @type app: callable following WSGI interface 88 @param app: next middleware application in the chain 89 @type global_conf: dict 90 @param global_conf: PasteDeploy global configuration dictionary 70 71 def initialise(self, prefix=DEFAULT_PARAM_PREFIX, **app_conf): 72 """Set-up Authorization Service middleware from keyword settings 73 91 74 @type prefix: basestring 92 75 @param prefix: prefix for configuration items … … 95 78 dictionary 96 79 """ 97 cls = Auth zServiceMiddleware80 cls = AuthorisationServiceMiddleware 98 81 99 82 # Loop based assignment where possible … … 103 86 104 87 self.queryInterface = self.createQueryInterface() 105 106 # Initialise the Authorisation Policy 88 89 # Initialise the Policy Information Point 90 pipCfgPrefix = prefix + cls.PIP_CFG_PREFIX 91 pip = PIP.fromConfig(app_conf, prefix=pipCfgPrefix) 92 107 93 policyFilePathOptName = prefix + cls.POLICY_FILEPATH_OPTNAME 108 94 policyFilePath = app_conf.get(policyFilePathOptName) 109 95 if policyFilePath is None: 110 raise AuthzServiceMiddlewareConfigError('No policy file path set - ' 111 '"policyFilePath" option ' 112 'name') 113 114 self.policyFilePath = policyFilePath 115 116 # Initialise the Policy Information Point 117 pipCfgPrefix = prefix + cls.PIP_CFG_PREFIX 118 pip = PIP.fromConfig(app_conf, prefix=pipCfgPrefix) 119 120 # Initialise the PDP reading in the policy 121 self.pdp = PDP.fromPolicySource(self.policyFilePath, ReaderFactory) 96 raise AuthorisationServiceMiddlewareConfigError("No XACML policy " 97 "file set") 98 99 # Initialise the XACML Context handler 100 ctxHandlerPrefix = prefix + \ 101 self.__class__.XACML_CTX_HANDLER_PARAM_PREFIX 102 lenCtxHandlerPrefix = len(ctxHandlerPrefix) 103 104 for optName, value in app_conf.items(): 105 if optName.startswith(ctxHandlerPrefix): 106 attrName = optName[lenCtxHandlerPrefix:] 107 setattr(self.__xacmlCtxHandler, attrName, value) 108 109 # Read XACML policy into PDP 110 self.__xacmlCtxHandler.pdp = PDP.fromPolicySource(policyFilePath, 111 XacmlEtreePolicyReaderFactory) 122 112 123 113 @classmethod … … 137 127 ''' 138 128 app = cls(app) 139 app.initialise( global_conf,**app_conf)129 app.initialise(**app_conf) 140 130 141 131 return app … … 154 144 return self._app(environ, start_response) 155 145 156 _getPolicyFilePath = lambda self: self.__policyFilePath157 158 def _setPolicyFilePath(self, value):159 if not isinstance(value, basestring):160 raise TypeError('Expecting string type for "policyFilePath" '161 'attribute; got %r' % type(value))162 self.__policyFilePath = value163 164 policyFilePath = property(_getPolicyFilePath,165 _setPolicyFilePath,166 doc="Policy file path")167 168 def _getIssuerFormat(self):169 if self.__issuerProxy is None:170 return None171 else:172 return self.__issuerProxy.value173 174 def _setIssuerFormat(self, value):175 if self.__issuerProxy is None:176 self.__issuerProxy = Issuer()177 178 self.__issuerProxy.format = value179 180 issuerFormat = property(_getIssuerFormat, _setIssuerFormat,181 doc="Issuer format")182 #183 # def _getIssuerName(self):184 # if self.__issuerProxy is None:185 # return None186 # else:187 # return self.__issuerProxy.value188 #189 # def _setIssuerName(self, value):190 # if self.__issuerProxy is None:191 # self.__issuerProxy = Issuer()192 #193 # self.__issuerProxy.value = value194 #195 # issuerName = property(_getIssuerName, _setIssuerName,196 # doc="Name of issuer of SAML Authorisation Query "197 # "Response")198 199 _getAssertionLifetime = lambda self: self.__assertionLifetime200 201 def _setAssertionLifetime(self, value):202 if isinstance(value, (int, float, basestring)):203 self.__assertionLifetime = float(value)204 else:205 raise TypeError('Expecting int, float or string type for '206 '"assertionLifetime" attribute; got %s instead' %207 type(value))208 209 assertionLifetime = property(fget=_getAssertionLifetime,210 fset=_setAssertionLifetime,211 doc="lifetime of assertion in seconds used to "212 "set assertion conditions notOnOrAfter "213 "time")214 215 def _getPdp(self):216 return self.__pdp217 218 def _setPdp(self, value):219 if not isinstance(value, PDPInterface):220 raise TypeError('Expecting %r type for "pdp" attribute; got %r '221 'instead' % (PDPInterface, value))222 223 self.__pdp = value224 225 pdp = property(_getPdp, _setPdp, None, "Policy Decision Point")226 146 227 147 def _get_queryInterfaceKeyName(self): … … 269 189 """ 270 190 271 # Nest function within Auth zServiceMiddleware method so that self is272 # in its scope191 # Nest function within AuthorisationServiceMiddleware method so that 192 # self is in its scope 273 193 def getAuthzDecision(authzDecisionQuery, samlResponse): 274 194 """Authorisation decision function accepts a SAML AuthzDecisionQuery 275 and calls the Policy Decision Point returning a response 276 277 @type authzDecisionQuery: saml.saml2.core.AuthzDecisionQuery 195 and calls the XACML context handler returning a response. The 196 context handler is an interface to the the XACML Policy Decision 197 Point, XACML polic(y|ies) and Policy Information Point. 198 199 @type authzDecisionQuery: ndg.saml.saml2.core.AuthzDecisionQuery 278 200 @param authzDecisionQuery: WSGI environment variables dictionary 279 @rtype: saml.saml2.core.Response201 @rtype: ndg.saml.saml2.core.Response 280 202 @return: SAML response containing Authorisation Decision Statement 281 """ 282 request = self._createXacmlRequestCtx(authzDecisionQuery) 283 284 # Call the PDP 285 xacmlResponse = self.pdp.evaluate(request) 286 287 # Create the SAML Response 288 self._createSAMLResponseAssertion(authzDecisionQuery, samlResponse) 289 authzDecisionStatement = samlResponse.assertions[0 290 ].authzDecisionStatements[0] 291 292 if (xacmlResponse.results[0].decision == 293 context.result.Decision.PERMIT): 294 log.info("AuthzServiceMiddleware.__call__: PDP granted " 295 "access for URI path [%s] using policy [%s]", 296 authzDecisionQuery.resource, 297 self.policyFilePath) 298 299 authzDecisionStatement.decision = DecisionType.PERMIT 300 301 elif (xacmlResponse.results[0].decision == 302 context.result.Decision.INDETERMINATE): 303 log.info("AuthzServiceMiddleware.__call__: PDP returned a " 304 "status of [%s] denying access for URI path [%s] " 305 "using policy [%s]", 306 context.result.Decision.INDETERMINATE, 307 authzDecisionQuery.resource, 308 self.policyFilePath) 309 310 authzDecisionStatement.decision = DecisionType.INDETERMINATE 311 312 else: 313 log.info("AuthzServiceMiddleware.__call__: PDP returned a " 314 "status of [%s] denying access for URI path [%s] " 315 "using policy [%s]", 316 context.result.Decision.DENY, 317 authzDecisionQuery.resource, 318 self.policyFilePath) 319 320 authzDecisionStatement.decision = DecisionType.DENY 321 322 return samlResponse 323 203 """ 204 # Create special request object which enables the context handler 205 # to formulate a response from the query and the existing response 206 # object initialised by this object 207 request = saml_ctx_handler.SamlPEPRequest() 208 request.authzDecisionQuery = authzDecisionQuery 209 request.response = samlResponse 210 211 response = self.__xacmlCtxHandler.handlePEPRequest(request) 212 213 return response 214 324 215 return getAuthzDecision 325 326 def _createXacmlRequestCtx(self, authzDecisionQuery):327 """Translate SAML authorisation decision query into a XACML request328 context329 """330 request = context.request.Request()331 subject = context.subject.Subject()332 333 attributeValueFactory = AttributeValueClassFactory()334 335 openidSubjectAttribute = Attribute()336 roleAttribute = Attribute()337 338 openidSubjectAttribute.attributeId = \339 authzDecisionQuery.subject.nameID.format340 341 AnyUriAttributeValue = attributeValueFactory(342 'http://www.w3.org/2001/XMLSchema#anyURI')343 344 openidSubjectAttribute.dataType = AnyUriAttributeValue.IDENTIFIER345 346 openidSubjectAttribute.attributeValues.append(AnyUriAttributeValue())347 openidSubjectAttribute.attributeValues[-1].value = \348 authzDecisionQuery.subject.nameID.value349 350 subject.attributes.append(openidSubjectAttribute)351 352 StringAttributeValue = attributeValueFactory(353 'http://www.w3.org/2001/XMLSchema#string')354 355 # TODO: get attributes - replace hard coded values356 roleAttribute.attributeId = "urn:ndg:security:authz:1.0:attr"357 roleAttribute.dataType = StringAttributeValue.IDENTIFIER358 359 roleAttribute.attributeValues.append(StringAttributeValue())360 roleAttribute.attributeValues[-1].value = 'staff'361 362 subject.attributes.append(roleAttribute)363 364 request.subjects.append(subject)365 366 resource = Resource()367 resourceAttribute = Attribute()368 resource.attributes.append(resourceAttribute)369 370 resourceAttribute.attributeId = \371 "urn:oasis:names:tc:xacml:1.0:resource:resource-id"372 373 resourceAttribute.dataType = AnyUriAttributeValue.IDENTIFIER374 resourceAttribute.attributeValues.append(AnyUriAttributeValue())375 resourceAttribute.attributeValues[-1].value = \376 authzDecisionQuery.resource377 378 request.resources.append(resource)379 380 request.action = context.action.Action()381 actionAttribute = Attribute()382 request.action.attributes.append(actionAttribute)383 384 actionAttribute.attributeId = \385 "urn:oasis:names:tc:xacml:1.0:action:action-id"386 actionAttribute.dataType = StringAttributeValue.IDENTIFIER387 actionAttribute.attributeValues.append(StringAttributeValue())388 actionAttribute.attributeValues[-1].value = authzDecisionQuery.actions[0389 ].value390 391 return request392 393 def _createSAMLResponseAssertion(self, authzDecisionQuery, response):394 """Helper method to add an assertion containing an Authorisation395 Decision Statement to the SAML response396 397 @param authzDecisionQuery: SAML Authorisation Decision Query398 @type authzDecisionQuery: ndg.saml.saml2.core.AuthzDecisionQuery399 @param response: SAML response400 @type response: ndg.saml.saml2.core.Response401 """402 # response = Response()403 #404 now = datetime.utcnow()405 response.issueInstant = now406 #407 # # Make up a request ID that this response is responding to408 # response.inResponseTo = authzDecisionQuery.id409 # response.id = str(uuid4())410 # response.version = SAMLVersion(SAMLVersion.VERSION_20)411 #412 # response.issuer = Issuer()413 # response.issuer.format = self.issuerFormat414 # response.issuer.value = self.issuerName415 #416 # response.status = Status()417 # response.status.statusCode = StatusCode()418 # response.status.statusMessage = StatusMessage()419 #420 # response.status.statusCode.value = StatusCode.SUCCESS_URI421 # response.status.statusMessage.value = ("Response created "422 # "successfully")423 424 assertion = Assertion()425 response.assertions.append(assertion)426 427 assertion.version = SAMLVersion(SAMLVersion.VERSION_20)428 assertion.id = str(uuid4())429 assertion.issueInstant = now430 431 # Add a conditions statement for a validity of 8 hours432 assertion.conditions = Conditions()433 assertion.conditions.notBefore = now434 assertion.conditions.notOnOrAfter = now + timedelta(435 seconds=self.assertionLifetime)436 437 assertion.subject = Subject()438 assertion.subject.nameID = NameID()439 assertion.subject.nameID.format = \440 authzDecisionQuery.subject.nameID.format441 assertion.subject.nameID.value = \442 authzDecisionQuery.subject.nameID.value443 444 authzDecisionStatement = AuthzDecisionStatement()445 assertion.authzDecisionStatements.append(authzDecisionStatement)446 447 authzDecisionStatement.resource = authzDecisionQuery.resource448 449 for action in authzDecisionQuery.actions:450 authzDecisionStatement.actions.append(Action())451 authzDecisionStatement.actions[-1].namespace = action.namespace452 authzDecisionStatement.actions[-1].value = action.value453 454 return response -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/xacml/ctx_handler/saml_ctx_handler.py
r7077 r7257 1 ''' 2 Created on 14 May 2010 3 4 @author: pjkersha 5 ''' 6 from ndg.xacml.core.context.handler import CtxHandlerInterface 7 8 9 class SamlCtxHandler(CtxHandlerInterface): 1 """XACML Context handler translates to and from SAML Authorisation Decision 2 Query / Response 3 4 """ 5 __author__ = "P J Kershaw" 6 __date__ = "14/05/10" 7 __copyright__ = "(C) 2010 Science and Technology Facilities Council" 8 __license__ = "BSD - see LICENSE file in top-level directory" 9 __contact__ = "Philip.Kershaw@stfc.ac.uk" 10 __revision__ = '$Id$' 11 import logging 12 log = logging.getLogger(__name__) 13 14 from datetime import datetime, timedelta 15 from uuid import uuid4 16 17 from ndg.saml.saml2 import core as _saml 18 from ndg.saml.common import SAMLVersion 19 20 from ndg.xacml.core import context as _xacmlContext 21 from ndg.xacml.core.attribute import Attribute as XacmlAttribute 22 from ndg.xacml.core.attributevalue import AttributeValueClassFactory as \ 23 XacmlAttributeValueClassFactory 24 from ndg.xacml.parsers.etree.factory import ReaderFactory 25 26 27 class SamlPEPRequest(object): 28 """Helper class for SamlCtxHandler.handlePEPRequest""" 29 __slots__ = ('__authzDecisionQuery', '__response', '__policyFilePath') 30 31 def __init__(self): 32 self.__authzDecisionQuery = None 33 self.__response = None 34 35 def _getAuthzDecisionQuery(self): 36 return self.__authzDecisionQuery 37 38 def _setAuthzDecisionQuery(self, value): 39 if not isinstance(value, _saml.AuthzDecisionQuery): 40 raise TypeError('Expecting %r type for "response" attribute, got %r' 41 % (_saml.Response, type(value))) 42 self.__authzDecisionQuery = value 43 44 authzDecisionQuery = property(_getAuthzDecisionQuery, 45 _setAuthzDecisionQuery, 46 doc="SAML Authorisation Decision Query") 47 48 def _getResponse(self): 49 return self.__response 50 51 def _setResponse(self, value): 52 if not isinstance(value, _saml.Response): 53 raise TypeError('Expecting %r type for "response" attribute, got %r' 54 % (_saml.Response, type(value))) 55 self.__response = value 56 57 response = property(_getResponse, _setResponse, doc="SAML Response") 58 59 60 class SamlCtxHandler(_xacmlContext.handler.CtxHandlerBase): 10 61 """XACML Context handler for accepting SAML 2.0 based authorisation 11 62 decision queries and interfacing to a PEP with SAML based Attribute Query 12 63 Interface 13 64 """ 14 def pepQuery(self, request, designator): 65 __slots__ = ( 66 '__policyFilePath', 67 '__issuerProxy', 68 '__assertionLifetime', 69 ) 70 71 def __init__(self): 72 super(SamlCtxHandler, self).__init__() 73 74 # Proxy object for SAML Response Issuer attributes. By generating a 75 # proxy the Response objects inherent attribute validation can be 76 # applied to Issuer related config parameters before they're assigned to 77 # the response issuer object generated in the authorisation decision 78 # query response 79 self.__issuerProxy = _saml.Issuer() 80 self.__assertionLifetime = 0. 81 82 def _getIssuerFormat(self): 83 if self.__issuerProxy is None: 84 return None 85 else: 86 return self.__issuerProxy.value 87 88 def _setIssuerFormat(self, value): 89 if self.__issuerProxy is None: 90 self.__issuerProxy = _saml.Issuer() 91 92 self.__issuerProxy.format = value 93 94 issuerFormat = property(_getIssuerFormat, _setIssuerFormat, 95 doc="Issuer format") 96 97 def _getIssuerName(self): 98 if self.__issuerProxy is None: 99 return None 100 else: 101 return self.__issuerProxy.value 102 103 def _setIssuerName(self, value): 104 if self.__issuerProxy is None: 105 self.__issuerProxy = _saml.Issuer() 106 107 self.__issuerProxy.value = value 108 109 issuerName = property(_getIssuerName, _setIssuerName, 110 doc="Name of issuer of SAML Authorisation Query " 111 "Response") 112 113 _getAssertionLifetime = lambda self: self.__assertionLifetime 114 115 def _setAssertionLifetime(self, value): 116 if isinstance(value, (int, float, long, basestring)): 117 self.__assertionLifetime = float(value) 118 else: 119 raise TypeError('Expecting int, long, float or string type for ' 120 '"assertionLifetime" attribute; got %s instead' % 121 type(value)) 122 123 assertionLifetime = property(fget=_getAssertionLifetime, 124 fset=_setAssertionLifetime, 125 doc="lifetime of assertion in seconds used to " 126 "set assertion conditions notOnOrAfter " 127 "time") 128 129 def handlePEPRequest(self, pepRequest): 130 """Handle request from Policy Enforcement Point 131 132 @param pepRequest: request containing a SAML authorisation decision 133 query and optionally an initialised SAML response object 134 @type pepRequest: ndg.security.server.xacml.saml_ctx_handler.SamlPEPRequest 135 @return: SAML authorisation decision response 136 @rtype: ndg.saml.saml2.core.Response 137 """ 138 samlAuthzDecisionQuery = pepRequest.authzDecisionQuery 139 140 xacmlRequest = self._createXacmlRequestCtx(samlAuthzDecisionQuery) 141 142 # Call the PDP 143 xacmlResponse = self.pdp.evaluate(xacmlRequest) 144 145 # Create the SAML Response 146 samlResponse = self._createSAMLResponseAssertion(samlAuthzDecisionQuery, 147 pepRequest.response) 148 149 samlAuthzDecisionStatement = samlResponse.assertions[0 150 ].authzDecisionStatements[0] 151 152 # Convert the decision status 153 if (xacmlResponse.results[0].decision == 154 _xacmlContext.result.Decision.PERMIT): 155 log.info("PDP granted access for URI path [%s]", 156 samlAuthzDecisionQuery.resource) 157 158 samlAuthzDecisionStatement.decision = _saml.DecisionType.PERMIT 159 160 elif (xacmlResponse.results[0].decision == 161 _xacmlContext.result.Decision.INDETERMINATE): 162 log.info("PDP returned a status of [%s] denying access for URI " 163 "path [%s]", _xacmlContext.result.Decision.INDETERMINATE, 164 samlAuthzDecisionQuery.resource) 165 166 samlAuthzDecisionStatement.decision = \ 167 _saml.DecisionType.INDETERMINATE 168 else: 169 log.info("PDP returned a status of [%s] denying access for URI " 170 "path [%s]", _xacmlContext.result.Decision.DENY, 171 samlAuthzDecisionQuery.resource) 172 173 samlAuthzDecisionStatement.decision = _saml.DecisionType.DENY 174 175 return samlResponse 176 177 def pipQuery(self, request, designator): 15 178 """Query a Policy Information Point to retrieve the attribute values 16 179 corresponding to the specified input designator. Optionally, update the … … 19 182 """ 20 183 return [] 184 185 def _createXacmlRequestCtx(self, samlAuthzDecisionQuery): 186 """Translate SAML authorisation decision query into a XACML request 187 context 188 """ 189 xacmlRequest = _xacmlContext.request.Request() 190 xacmlSubject = _xacmlContext.subject.Subject() 191 192 xacmlAttributeValueFactory = XacmlAttributeValueClassFactory() 193 194 openidSubjectAttribute = XacmlAttribute() 195 roleAttribute = XacmlAttribute() 196 197 openidSubjectAttribute.attributeId = \ 198 samlAuthzDecisionQuery.subject.nameID.format 199 200 XacmlAnyUriAttributeValue = xacmlAttributeValueFactory( 201 'http://www.w3.org/2001/XMLSchema#anyURI') 202 203 openidSubjectAttribute.dataType = XacmlAnyUriAttributeValue.IDENTIFIER 204 205 openidSubjectAttribute.attributeValues.append( 206 XacmlAnyUriAttributeValue()) 207 openidSubjectAttribute.attributeValues[-1].value = \ 208 samlAuthzDecisionQuery.subject.nameID.value 209 210 xacmlSubject.attributes.append(openidSubjectAttribute) 211 212 XacmlStringAttributeValue = xacmlAttributeValueFactory( 213 'http://www.w3.org/2001/XMLSchema#string') 214 215 # TODO: get attributes - replace hard coded values 216 roleAttribute.attributeId = "urn:ndg:security:authz:1.0:attr" 217 roleAttribute.dataType = XacmlStringAttributeValue.IDENTIFIER 218 219 roleAttribute.attributeValues.append(XacmlStringAttributeValue()) 220 roleAttribute.attributeValues[-1].value = 'staff' 221 222 xacmlSubject.attributes.append(roleAttribute) 223 224 xacmlRequest.subjects.append(xacmlSubject) 225 226 resource = _xacmlContext.resource.Resource() 227 resourceAttribute = XacmlAttribute() 228 resource.attributes.append(resourceAttribute) 229 230 resourceAttribute.attributeId = \ 231 "urn:oasis:names:tc:xacml:1.0:resource:resource-id" 232 233 resourceAttribute.dataType = XacmlAnyUriAttributeValue.IDENTIFIER 234 resourceAttribute.attributeValues.append(XacmlAnyUriAttributeValue()) 235 resourceAttribute.attributeValues[-1].value = \ 236 samlAuthzDecisionQuery.resource 237 238 xacmlRequest.resources.append(resource) 239 240 xacmlRequest.action = _xacmlContext.action.Action() 241 242 for action in samlAuthzDecisionQuery.actions: 243 xacmlActionAttribute = XacmlAttribute() 244 xacmlRequest.action.attributes.append(xacmlActionAttribute) 245 246 xacmlActionAttribute.attributeId = \ 247 "urn:oasis:names:tc:xacml:1.0:action:action-id" 248 xacmlActionAttribute.dataType = XacmlStringAttributeValue.IDENTIFIER 249 xacmlActionAttribute.attributeValues.append( 250 XacmlStringAttributeValue()) 251 xacmlActionAttribute.attributeValues[-1].value = action.value 252 253 return xacmlRequest 254 255 def _createSAMLResponseAssertion(self, authzDecisionQuery, response): 256 """Helper method to add an assertion containing an Authorisation 257 Decision Statement to the SAML response 258 259 @param authzDecisionQuery: SAML Authorisation Decision Query 260 @type authzDecisionQuery: ndg.saml.saml2.core.AuthzDecisionQuery 261 @param response: SAML response 262 @type response: ndg.saml.saml2.core.Response 263 """ 264 265 # Check for a response set, if none present create one. 266 if response is None: 267 response = _saml.Response() 268 269 now = datetime.utcnow() 270 response.issueInstant = now 271 272 # Make up a request ID that this response is responding to 273 response.inResponseTo = authzDecisionQuery.id 274 response.id = str(uuid4()) 275 response.version = SAMLVersion(SAMLVersion.VERSION_20) 276 277 response.issuer = _saml.Issuer() 278 response.issuer.format = self.issuerFormat 279 response.issuer.value = self.issuerName 280 281 response.status = _saml.Status() 282 response.status.statusCode = _saml.StatusCode() 283 response.status.statusMessage = _saml.StatusMessage() 284 285 response.status.statusCode.value = _saml.StatusCode.SUCCESS_URI 286 response.status.statusMessage.value = ("Response created " 287 "successfully") 288 289 assertion = _saml.Assertion() 290 response.assertions.append(assertion) 291 292 assertion.version = SAMLVersion(SAMLVersion.VERSION_20) 293 assertion.id = str(uuid4()) 294 295 now = datetime.utcnow() 296 assertion.issueInstant = now 297 298 # Add a conditions statement for a validity of 8 hours 299 assertion.conditions = _saml.Conditions() 300 assertion.conditions.notBefore = now 301 assertion.conditions.notOnOrAfter = now + timedelta( 302 seconds=self.assertionLifetime) 303 304 assertion.subject = _saml.Subject() 305 assertion.subject.nameID = _saml.NameID() 306 assertion.subject.nameID.format = \ 307 authzDecisionQuery.subject.nameID.format 308 assertion.subject.nameID.value = \ 309 authzDecisionQuery.subject.nameID.value 310 311 authzDecisionStatement = _saml.AuthzDecisionStatement() 312 assertion.authzDecisionStatements.append(authzDecisionStatement) 313 314 authzDecisionStatement.resource = authzDecisionQuery.resource 315 316 for action in authzDecisionQuery.actions: 317 authzDecisionStatement.actions.append(_saml.Action()) 318 authzDecisionStatement.actions[-1].namespace = action.namespace 319 authzDecisionStatement.actions[-1].value = action.value 320 321 return response -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/config/authorisationservice/authorisation-service.ini
r7168 r7257 37 37 # This filter is a container for a binding to a SOAP based interface to the 38 38 # Attribute Authority 39 paste.filter_app_factory = ndg.saml.test.binding.soap.test_authzservice:TestAuthorisationServiceMiddleware 40 queryInterfaceKeyName = AUTHZ_DECISION_QUERY_FUNC 39 paste.filter_app_factory = ndg.security.server.wsgi.authz.service:AuthorisationServiceMiddleware.filter_app_factory 40 prefix = authz. 41 authz.queryInterfaceKeyName = AUTHZ_DECISION_QUERY_FUNC 42 authz.policyFilePath = %(here)s/policy.xml 43 authz.xacmlContext.assertionLifetime = 86400 41 44 42 45 # Logging configuration -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/test_authz.py
r7168 r7257 41 41 from ndg.saml.xml.etree import (AuthzDecisionQueryElementTree, 42 42 ResponseElementTree) 43 43 44 44 45 class TestAuthorisationServiceMiddleware(object): … … 124 125 return authzDecisionQuery 125 126 127 126 128 class RedirectFollowingAccessDenied(PEPResultHandlerMiddleware): 127 129 … … 141 143 else: 142 144 return super(RedirectFollowingAccessDenied, self).__call__( 143 environ,144 start_response)145 146 145 environ, 146 start_response) 147 148 147 149 class TestAuthZMiddleware(object): 148 150 '''Test Application for the Authentication handler to protect''' … … 177 179 str(len(TestAuthZMiddleware.response))), 178 180 ('Content-type', 'text/plain')]) 181 179 182 return [TestAuthZMiddleware.response] 180 183 … … 195 198 BaseTestCase.__init__(self, *args, **kwargs) 196 199 197 198 200 wsgiapp = loadapp('config:'+SamlWSGIAuthZTestCase.INI_FILE, 199 201 relative_to=SamlWSGIAuthZTestCase.THIS_DIR) … … 238 240 239 241 def test04Catch403WithLoggedIn(self): 240 241 242 # Check that the application being secured can raise a HTTP 403 242 243 # response and that this respected by the Authorization middleware … … 245 246 extra_environ = { 246 247 self.__class__.SESSION_KEYNAME: 247 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI) 248 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 249 'REMOTE_USER': self.__class__.OPENID_URI 248 250 } 249 251 response = self.app.get('/test_403', … … 252 254 253 255 def test05Catch401WithNotLoggedInAndSecuredURI(self): 254 255 # AuthZ middleware grants access because the URI requested is not 256 # targeted in the policy 256 # AuthZ middleware grants access because the URI requested is has no 257 # subject restriction set in the policy rule 257 258 258 259 # AuthZ middleware checks for username key in session set by AuthN … … 269 270 extra_environ = { 270 271 self.__class__.SESSION_KEYNAME: 271 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI) 272 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 273 'REMOTE_USER': self.__class__.OPENID_URI 272 274 } 273 275 … … 285 287 extra_environ = { 286 288 self.__class__.SESSION_KEYNAME: 287 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI) 289 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 290 'REMOTE_USER': self.__class__.OPENID_URI 288 291 } 289 292 … … 300 303 extra_environ = { 301 304 self.__class__.SESSION_KEYNAME: 302 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI) 305 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 306 'REMOTE_USER': self.__class__.OPENID_URI 303 307 } 304 308 … … 347 351 # User is logged in but doesn't have the required credentials for 348 352 # access 349 extra_environ = { 350 self.__class__.SESSION_KEYNAME: 351 BeakerSessionStub(username=self.__class__.OPENID_URI) 352 } 353 354 # Expecting redirect response to specified redirect URI 355 response = self.app.get('/test_accessDeniedToSecuredURI', 356 extra_environ=extra_environ, 357 status=302) 358 print(response) 359 self.assert_(response.header_dict.get('location') == self.redirectURI) 353 raise NotImplementedError('TODO: fix recursion error') 354 # extra_environ = { 355 # self.__class__.SESSION_KEYNAME: 356 # BeakerSessionStub(username=self.__class__.OPENID_URI) 357 # } 358 # 359 # # Expecting redirect response to specified redirect URI 360 # response = self.app.get('/test_accessDeniedToSecuredURI', 361 # extra_environ=extra_environ, 362 # status=302) 363 # print(response) 364 # self.assert_(response.header_dict.get('location') == self.redirectURI) 360 365 361 366 if __name__ == "__main__":
Note: See TracChangeset
for help on using the changeset viewer.