Changeset 6264
- Timestamp:
- 05/01/10 09:45:34 (11 years ago)
- Location:
- TI12-security/trunk/NDGSecurity/python
- Files:
-
- 5 added
- 1 deleted
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authn.py
r6246 r6264 22 22 from paste.request import construct_url, parse_querystring 23 23 import authkit.authenticate 24 25 from ndg.security.server.wsgi import NDGSecurityMiddlewareBase, \ 26 NDGSecurityMiddlewareError, NDGSecurityMiddlewareConfigError 24 from authkit.authenticate.multi import MultiHandler 25 26 from ndg.security.server.wsgi import (NDGSecurityMiddlewareBase, 27 NDGSecurityMiddlewareError, 28 NDGSecurityMiddlewareConfigError) 29 from ndg.security.server.wsgi.session import (SessionMiddlewareBase, 30 SessionHandlerMiddleware) 31 27 32 28 33 class AuthnException(NDGSecurityMiddlewareError): … … 44 49 45 50 class HTTPBasicAuthMiddleware(NDGSecurityMiddlewareBase): 46 '''HTTP Basic Authentication Middleware 47 51 '''HTTP Basic Authentication Middleware 48 52 ''' 49 53 … … 178 182 # AuthKit based HTTP basic authentication plugin not currently needed but may 179 183 # need resurrecting 180 181 #from authkit.permissions import UserIn 182 #from ndg.security.server.wsgi.utils.sessionmanagerclient import \ 183 # WSGISessionManagerClient 184 # 185 #class HTTPBasicAuthentication(object): 186 # '''Authkit based HTTP Basic Authentication. __call__ defines a 187 # validation function to fit with the pattern for the AuthKit interface 188 # ''' 189 # 190 # def __init__(self): 191 # self._userIn = UserIn([]) 192 # 193 # def __call__(self, environ, username, password): 194 # """validation function""" 195 # try: 196 # client = WSGISessionManagerClient(environ=environ, 197 # environKeyName=self.sessionManagerFilterID) 198 # res = client.connect(username, passphrase=password) 199 # 200 # if username not in self._userIn.users: 201 # self._userIn.users += [username] 202 # 203 # # TODO: set session 204 # 205 # except Exception, e: 206 # return False 207 # else: 208 # return True 209 210 211 class SessionMiddlewareBase(NDGSecurityMiddlewareBase): 212 """Base class for Authentication redirect middleware and Session Handler 213 middleware 214 215 @type propertyDefaults: dict 216 @cvar propertyDefaults: valid configuration property keywords 217 """ 218 propertyDefaults = { 219 'sessionKey': 'beaker.session.ndg.security' 220 } 221 222 _isAuthenticated = lambda self: \ 223 SessionMiddlewareBase.USERNAME_SESSION_KEYNAME in \ 224 self.environ.get(self.sessionKey, ()) 225 226 isAuthenticated = property(fget=_isAuthenticated, 227 doc='boolean to indicate is user logged in') 184 from authkit.permissions import UserIn 185 186 class HTTPBasicAuthentication(object): 187 '''Authkit based HTTP Basic Authentication. __call__ defines a 188 validation function to fit with the pattern for the AuthKit interface 189 ''' 190 191 def __init__(self): 192 self._userIn = UserIn([]) 193 194 def __call__(self, environ, username, password): 195 """AuthKit HTTP Basic Auth validation function - return True/False""" 196 raise NotImplementedError() 228 197 229 198 … … 253 222 propertyDefaults.update(AuthnRedirectMiddleware.propertyDefaults) 254 223 255 256 224 TRIGGER_HTTP_STATUS_CODE = '401' 257 225 MIDDLEWARE_ID = 'AuthnRedirectInitiatorMiddleware' … … 269 237 dictionary 270 238 ''' 271 self._ redirectURI = None239 self.__redirectURI = None 272 240 super(AuthnRedirectInitiatorMiddleware, self).__init__(app, 273 241 global_conf, 274 242 **app_conf) 275 243 276 244 @NDGSecurityMiddlewareBase.initCall 277 245 def __call__(self, environ, start_response): … … 292 260 raise TypeError("Redirect URI must be set to string type") 293 261 294 self._ redirectURI = uri262 self.__redirectURI = uri 295 263 296 264 def _getRedirectURI(self): 297 return self._ redirectURI265 return self.__redirectURI 298 266 299 267 redirectURI = property(fget=_getRedirectURI, 300 fset=_setRedirectURI, 301 doc="URI to redirect to if user is not authenticated") 268 fset=_setRedirectURI, 269 doc="URI to redirect to if user is not " 270 "authenticated") 302 271 303 272 def _setRedirectResponse(self): … … 404 373 return super(AuthKitRedirectResponseMiddleware, self).__call__(environ, 405 374 start_response) 406 407 408 class SessionHandlerMiddlewareError(AuthnException): 409 """Base exception for SessionHandlerMiddleware""" 410 411 class SessionHandlerMiddlewareConfigError(SessionHandlerMiddlewareError): 412 """Configuration errors from SessionHandlerMiddleware""" 413 414 class OpenIdAXConfigError(SessionHandlerMiddlewareError): 415 """Error parsing OpenID Ax (Attribute Exchange) parameters""" 416 417 418 class SessionHandlerMiddleware(SessionMiddlewareBase): 419 '''Middleware to: 420 - establish user session details following redirect from OpenID Relying 421 Party sign-in or SSL Client authentication 422 - end session redirecting back to referrer URI following call to a logout 423 URI as implemented in AuthKit 424 ''' 425 AX_SESSION_KEYNAME = 'openid.ax' 426 SM_URI_SESSION_KEYNAME = 'sessionManagerURI' 427 ID_SESSION_KEYNAME = 'sessionId' 428 PEP_CTX_SESSION_KEYNAME = 'pepCtx' 429 CREDENTIAL_WALLET_SESSION_KEYNAME = 'credentialWallet' 430 431 SESSION_KEYNAMES = ( 432 SessionMiddlewareBase.USERNAME_SESSION_KEYNAME, 433 SM_URI_SESSION_KEYNAME, 434 ID_SESSION_KEYNAME, 435 PEP_CTX_SESSION_KEYNAME, 436 CREDENTIAL_WALLET_SESSION_KEYNAME 437 ) 438 439 AX_KEYNAME = 'ax' 440 SM_URI_AX_KEYNAME = 'value.sessionManagerURI.1' 441 SESSION_ID_AX_KEYNAME = 'value.sessionId.1' 442 443 AUTHKIT_COOKIE_SIGNOUT_PARAMNAME = 'authkit.cookie.signoutpath' 444 SIGNOUT_PATH_PARAMNAME = 'signoutPath' 445 SESSION_KEY_PARAMNAME = 'sessionKey' 446 propertyDefaults = { 447 SIGNOUT_PATH_PARAMNAME: None, 448 SESSION_KEY_PARAMNAME: 'beaker.session.ndg.security' 449 } 450 451 AUTH_TKT_SET_USER_ENVIRON_KEYNAME = 'paste.auth_tkt.set_user' 452 453 PARAM_PREFIX = 'sessionHandler.' 454 455 def __init__(self, app, global_conf, prefix=PARAM_PREFIX, **app_conf): 456 ''' 457 @type app: callable following WSGI interface 458 @param app: next middleware application in the chain 459 @type global_conf: dict 460 @param global_conf: PasteDeploy global configuration dictionary 461 @type prefix: basestring 462 @param prefix: prefix for configuration items 463 @type app_conf: dict 464 @param app_conf: PasteDeploy application specific configuration 465 dictionary 466 ''' 467 signoutPathParamName = prefix + \ 468 SessionHandlerMiddleware.SIGNOUT_PATH_PARAMNAME 469 470 if signoutPathParamName not in app_conf: 471 authKitSignOutPath = app_conf.get( 472 SessionHandlerMiddleware.AUTHKIT_COOKIE_SIGNOUT_PARAMNAME) 473 474 if authKitSignOutPath: 475 app_conf[signoutPathParamName] = authKitSignOutPath 476 477 log.info('Set signoutPath=%s from "%s" setting', 478 authKitSignOutPath, 479 SessionHandlerMiddleware.AUTHKIT_COOKIE_SIGNOUT_PARAMNAME) 480 else: 481 raise SessionHandlerMiddlewareConfigError( 482 '"signoutPath" parameter is not set') 483 484 super(SessionHandlerMiddleware, self).__init__(app, 485 global_conf, 486 prefix=prefix, 487 **app_conf) 488 489 @NDGSecurityMiddlewareBase.initCall 490 def __call__(self, environ, start_response): 491 """Manage setting of session from AuthKit following OpenID Relying 492 Party sign in and manage logout 493 494 @type environ: dict 495 @param environ: WSGI environment variables dictionary 496 @type start_response: function 497 @param start_response: standard WSGI start response function 498 """ 499 log.debug("SessionHandlerMiddleware.__call__ ...") 500 501 session = environ.get(self.sessionKey) 502 if session is None: 503 raise SessionHandlerMiddlewareConfigError( 504 'SessionHandlerMiddleware.__call__: No beaker session key ' 505 '"%s" found in environ' % self.sessionKey) 506 507 if self.signoutPath and self.pathInfo == self.signoutPath: 508 log.debug("SessionHandlerMiddleware.__call__: caught sign out " 509 "path [%s]", self.signoutPath) 510 511 referrer = environ.get('HTTP_REFERER') 512 if referrer is not None: 513 def _start_response(status, header, exc_info=None): 514 """Alter the header to send a redirect to the logout 515 referrer address""" 516 filteredHeader = [(field, val) for field, val in header 517 if field.lower() != 'location'] 518 filteredHeader.extend([('Location', referrer)]) 519 return start_response(self.getStatusMessage(302), 520 filteredHeader, 521 exc_info) 522 523 else: 524 log.error('No referrer set for redirect following logout') 525 _start_response = start_response 526 527 # Clear user details from beaker session 528 for keyName in self.__class__.SESSION_KEYNAMES: 529 session.pop(keyName, None) 530 session.save() 531 else: 532 log.debug("SessionHandlerMiddleware.__call__: checking for " 533 "REMOTE_* environment variable settings set by OpenID " 534 "Relying Party signin...") 535 536 if SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME not in session\ 537 and SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME in environ: 538 log.debug("SessionHandlerMiddleware.__call__: updating session " 539 "username=%s", environ[ 540 SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME]) 541 542 session[SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME 543 ] = environ[ 544 SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME] 545 session.save() 546 547 remoteUserData = environ.get( 548 SessionHandlerMiddleware.USERDATA_ENVIRON_KEYNAME, '') 549 if remoteUserData: 550 log.debug("SessionHandlerMiddleware.__call__: found " 551 "REMOTE_USER_DATA=%s, set from OpenID Relying Party " 552 "signin", 553 environ[ 554 SessionHandlerMiddleware.USERDATA_ENVIRON_KEYNAME 555 ]) 556 557 # eval is safe here because AuthKit cookie is signed and 558 # AuthKit middleware checks for tampering 559 if (SessionHandlerMiddleware.SM_URI_SESSION_KEYNAME not in 560 session or 561 SessionHandlerMiddleware.ID_SESSION_KEYNAME not in session): 562 563 axData = eval(remoteUserData) 564 if (isinstance(axData, dict) and 565 SessionHandlerMiddleware.AX_KEYNAME in axData): 566 567 ax = axData[SessionHandlerMiddleware.AX_KEYNAME] 568 569 # Save attributes keyed by attribute name 570 session[ 571 SessionHandlerMiddleware.AX_SESSION_KEYNAME 572 ] = SessionHandlerMiddleware._parseOpenIdAX(ax) 573 574 log.debug("SessionHandlerMiddleware.__call__: updated " 575 "session with OpenID AX values: %r" % 576 session[ 577 SessionHandlerMiddleware.AX_SESSION_KEYNAME 578 ]) 579 580 # Save Session Manager specific attributes 581 sessionManagerURI = ax.get( 582 SessionHandlerMiddleware.SM_URI_AX_KEYNAME) 583 584 session[SessionHandlerMiddleware.SM_URI_SESSION_KEYNAME 585 ] = sessionManagerURI 586 587 sessionId = ax.get( 588 SessionHandlerMiddleware.SESSION_ID_AX_KEYNAME) 589 session[SessionHandlerMiddleware.ID_SESSION_KEYNAME 590 ] = sessionId 591 592 session.save() 593 594 log.debug("SessionHandlerMiddleware.__call__: updated " 595 "session " 596 "with sessionManagerURI=%s and " 597 "sessionId=%s", 598 sessionManagerURI, 599 sessionId) 600 601 # Reset cookie removing user data 602 setUser = environ[ 603 SessionHandlerMiddleware.AUTH_TKT_SET_USER_ENVIRON_KEYNAME] 604 setUser( 605 session[SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME]) 606 else: 607 log.debug("SessionHandlerMiddleware.__call__: REMOTE_USER_DATA " 608 "is not set") 609 610 _start_response = start_response 611 612 return self._app(environ, _start_response) 613 614 @staticmethod 615 def _parseOpenIdAX(ax): 616 """Return a dictionary of attribute exchange attributes parsed from the 617 OpenID Provider response set in the REMOTE_USER_DATA AuthKit environ 618 key 619 620 @param ax: dictionary of AX parameters - format of keys is e.g. 621 count.paramName, value.paramName.<n>, type.paramName 622 @type ax: dict 623 @return: dictionary of parameters keyed by parameter with values for 624 each parameter a tuple of count.paramName values 625 @rtype: dict 626 """ 627 628 # Copy Attributes into session 629 outputKeys = [k.replace('type.', '') for k in ax.keys() 630 if k.startswith('type.')] 631 632 output = {} 633 for outputKey in outputKeys: 634 axCountKeyName = 'count.' + outputKey 635 axCount = int(ax[axCountKeyName]) 636 637 axValueKeyPrefix = 'value.%s.' % outputKey 638 output[outputKey] = tuple([v for k, v in ax.items() 639 if k.startswith(axValueKeyPrefix)]) 640 641 nVals = len(output[outputKey]) 642 if nVals != axCount: 643 raise OpenIdAXConfigError('Got %d parameters for AX attribute ' 644 '"%s"; but "%s" AX key is set to %d' 645 % (nVals, 646 axCountKeyName, 647 axCountKeyName, 648 axCount)) 649 650 return output 651 652 653 from authkit.authenticate.multi import MultiHandler 375 654 376 655 377 class AuthenticationMiddlewareConfigError(NDGSecurityMiddlewareConfigError): -
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/__init__.py
r6263 r6264 31 31 SessionHandlerMiddleware) 32 32 33 from ndg.security.server.wsgi.authz.result_handler.basic import \ 34 PEPResultHandlerMiddleware 35 33 36 from ndg.security.common.authz.msi import (Policy, PIP, PIPBase, 34 37 PIPAttributeQuery, 35 38 PIPAttributeResponse, PDP, Request, 36 39 Response, Resource, Subject) 37 38 39 class PEPResultHandlerMiddleware(SessionMiddlewareBase):40 """This middleware is invoked if access is denied to a given resource. It41 is incorporated into the call stack by passing it in to a MultiHandler42 instance. The MultiHandler is configured in the AuthorizationMiddlewareBase43 class below. The MultiHandler is passed a checker method which determines44 whether to allow access, or call this interface. The checker is45 implemented in the AuthorizationHandler. See below ...46 47 This class can be overridden to define custom behaviour for the access48 denied response e.g. include an interface to enable users to register for49 the dataset from which they have been denied access. See50 AuthorizationMiddlewareBase pepResultHandler keyword.51 52 SessionMiddlewareBase base class defines user session key and53 isAuthenticated property54 """55 56 def __init__(self, app, global_conf, prefix='', **app_conf):57 '''58 @type app: callable following WSGI interface59 @param app: next middleware application in the chain60 @type global_conf: dict61 @param global_conf: PasteDeploy global configuration dictionary62 @type prefix: basestring63 @param prefix: prefix for configuration items64 @type app_conf: dict65 @param app_conf: PasteDeploy application specific configuration66 dictionary67 '''68 super(PEPResultHandlerMiddleware, self).__init__(app,69 global_conf,70 prefix=prefix,71 **app_conf)72 73 @NDGSecurityMiddlewareBase.initCall74 def __call__(self, environ, start_response):75 76 log.debug("PEPResultHandlerMiddleware.__call__ ...")77 78 self.session = self.environ.get(self.sessionKey)79 if not self.isAuthenticated:80 # This check is included as a precaution: this condition should be81 # caught be the AuthNRedirectHandlerMiddleware or PEPFilter82 log.warning("PEPResultHandlerMiddleware: user is not "83 "authenticated - setting HTTP 401 response")84 return self._setErrorResponse(code=UNAUTHORIZED)85 else:86 # Get response message from PDP recorded by PEP87 pepCtx = self.session.get('pepCtx', {})88 pdpResponse = pepCtx.get('response')89 msg = getattr(pdpResponse, 'message', '')90 91 response = ("Access is forbidden for this resource:%s"92 "Please check with your site administrator that you "93 "have the required access privileges." %94 msg.join(('\n\n',)*2))95 96 return self._setErrorResponse(code=FORBIDDEN, msg=response)97 40 98 41 … … 117 60 MIDDLEWARE_ID = 'PEPFilter' 118 61 POLICY_PARAM_PREFIX = 'policy.' 62 119 63 SESSION_KEYNAME = 'sessionKey' 64 65 # Key names for PEP context information 66 PEPCTX_SESSION_KEYNAME = 'pepCtx' 67 PEPCTX_REQUEST_KEYNAME = 'request' 68 PEPCTX_RESPONSE_KEYNAME = 'response' 69 PEPCTX_TIMESTAMP_KEYNAME = 'timestamp' 120 70 POLICY_FILEPATH_PARAMNAME = 'filePath' 121 71 … … 220 170 # Record the result in the user's session to enable later 221 171 # interrogation by the AuthZResultHandlerMiddleware 222 session['pepCtx'] = {'request': request, 'response': response,223 'timestamp': time()}224 session.save()225 172 226 173 if response.status == Response.DECISION_PERMIT: … … 244 191 return self._setErrorResponse(code=triggerStatusCode) 245 192 193 @classmethod 194 def setSession(cls, session, save=True): 195 session[cls.PEPCTX_SESSION_KEYNAME] = { 196 cls.PEPCTX_REQUEST_KEYNAME: request, 197 cls.PEPCTX_RESPONSE_KEYNAME: response, 198 cls.PEPCTX_TIMESTAMP_KEYNAME: time() 199 } 200 201 if save: 202 session.save() 203 246 204 def _getMatchingTargets(self, resourceURI): 247 205 """This method may only be called following __call__ as __call__ -
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/test_authz.py
r6069 r6264 107 107 self.startSiteAAttributeAuthority() 108 108 109 110 111 109 def test01CatchNoBeakerSessionFound(self): 112 110 … … 215 213 print response 216 214 217 218 219 215 220 216 class TestAuthZMiddleware(object):
Note: See TracChangeset
for help on using the changeset viewer.