source: TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/__init__.py @ 7287

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/__init__.py@7287
Revision 7287, 6.1 KB checked in by pjkersha, 10 years ago (diff)

Incomplete - task 2: XACML-Security Integration

  • Working WSGI Authorisation filter with connection to SAML/XACML based Authorisation Service - unit tests: ndg.security.test.unit.wsgi.authz.test_authz
  • It may need some optimisation to avoid too many WS callouts to the Authorisation Service - perhaps add a local PDP to the authorisation filter to filter out some requests going over the wire e.g. requests for web page CSS or graphics content.
  • The XACML policy file has some big additions to it to support the various test conditions in ndg.security.test.unit.wsgi.authz.test_authz. These should be ported back to the ndg_xacml package unit tests.
  • Next major task: remove temp fix in XACML Context handler - instead of using hardwired roles for the user alter it so that the PDP makes a request back to the PIP (Policy Enforcement Point) to grab additional attributes. The PIP will call to Attibute Service(s) to pull any additional attributes needed/
  • Property svn:keywords set to Id
Line 
1"""WSGI Policy Enforcement Point Package
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "16/01/2009"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__revision__ = "$Id$"
10__license__ = "BSD - see LICENSE file in top-level directory"
11import logging
12log = logging.getLogger(__name__)
13
14import warnings
15from time import time
16from urlparse import urlunsplit
17import httplib
18
19from paste.cascade import Cascade
20from paste.urlparser import StaticURLParser
21from authkit.authenticate.multi import MultiHandler
22
23from ndg.security.common.utils.classfactory import importClass
24from ndg.security.common.credentialwallet import SAMLCredentialWallet
25from ndg.security.server.wsgi import NDGSecurityMiddlewareBase
26from ndg.security.server.wsgi.authz.pep import SamlPepFilter
27from ndg.security.server.wsgi.authz.result_handler import \
28    PEPResultHandlerMiddlewareBase
29from ndg.security.server.wsgi.authz.result_handler.basic import \
30    PEPResultHandlerMiddleware
31
32
33class Http403ForbiddenStatusHandler(object):
34    """Handler to catch HTTP 403 Forbidden responses.  It integrates with
35    AuthKit's MultiHandler.  This enables the given middleware to be substituted
36    into the WSGI stack should a 403 status be detected set from upstream
37    middleware.
38       
39    @cvar TRIGGER_HTTP_STATUS_CODE: status code to catch - HTTP 403 Forbidden
40    @type TRIGGER_HTTP_STATUS_CODE: basestring
41    """
42    TRIGGER_HTTP_STATUS_CODE = str(httplib.FORBIDDEN)
43   
44    @classmethod
45    def intercept(cls, environ, status, headers):
46        """Checker function for AuthKit Multihandler
47       
48        @type environ: dict
49        @param environ: WSGI environment dictionary
50        @type status: basestring
51        @param status: HTTP response code set by application middleware
52        that this intercept function is to protect
53        @type headers: list
54        @param headers: HTTP response header content"""
55       
56        if status.startswith(cls.TRIGGER_HTTP_STATUS_CODE):
57            log.debug("Found [%s] status for URI path [%s]: invoking access "
58                      "denied response",
59                      cls.TRIGGER_HTTP_STATUS_CODE,
60                      environ['PATH_INFO'])
61            return True
62        else:
63            # No match - it's publicly accessible
64            log.debug("The return status [%s] for this URI path [%s] didn't "
65                      "match the trigger status [%s]",
66                      status,
67                      environ['PATH_INFO'],
68                      cls.TRIGGER_HTTP_STATUS_CODE)
69            return False
70
71   
72class AuthorisationFilterConfigError(Exception):
73    """AuthorisationFilterBase configuration related exceptions"""
74 
75   
76class AuthorisationFilter(object):
77    '''NDG Security Authorisation filter wraps the Policy Enforcement Point
78    (PEP) filter to intercept requests and enforce access control decisions and
79    result handler middleware which enables a customised response given an
80    authorisation denied decision from the PEP filter.
81    '''
82    PEP_PARAM_PREFIX = 'pep.'
83    RESULT_HANDLER_PARAMNAME = "resultHandler"
84    RESULT_HANDLER_PARAM_PREFIX = RESULT_HANDLER_PARAMNAME + '.'
85    RESULT_HANDLER_STATIC_CONTENT_DIR_PARAMNAME = 'staticContentDir'
86   
87    @classmethod
88    def filter_app_factory(cls, app, global_conf, prefix='', **app_conf):
89        """Set-up Policy Enforcement Point to enforce access control decisions
90        based on the URI path requested and/or the HTTP response code set by
91        application(s) to be protected.  An AuthKit MultiHandler is setup to
92        handle the latter.  PEPResultHandlerMiddleware handles the output
93        set following an access denied decision
94        @type app: callable following WSGI interface
95        @param app: next middleware application in the chain     
96        @type global_conf: dict       
97        @param global_conf: PasteDeploy global configuration dictionary
98        @type prefix: basestring
99        @param prefix: prefix for configuration items
100        @type app_conf: dict       
101        @param app_conf: PasteDeploy application specific configuration
102        dictionary
103        """
104        # Allow for static content for use with PEP result handler middleware
105        resultHandlerParamPrefix = prefix + cls.RESULT_HANDLER_PARAM_PREFIX
106        resultHandlerStaticContentDirParamName = \
107                                resultHandlerParamPrefix + \
108                                cls.RESULT_HANDLER_STATIC_CONTENT_DIR_PARAMNAME
109       
110        resultHandlerStaticContentDir = app_conf.get(
111                                    resultHandlerStaticContentDirParamName)
112        if resultHandlerStaticContentDir is not None:   
113            staticApp = StaticURLParser(resultHandlerStaticContentDir)
114            app = Cascade([app, staticApp], catch=(httplib.NOT_FOUND,))
115
116        pepPrefix = prefix + cls.PEP_PARAM_PREFIX
117        pepFilter = SamlPepFilter.filter_app_factory(app, 
118                                                     global_conf, 
119                                                     prefix=pepPrefix, 
120                                                     **app_conf)
121       
122        # Now add the multi-handler to enable result handler to be invoked on
123        # 403 forbidden status from upstream middleware
124        app = MultiHandler(pepFilter)
125
126        resultHandlerClassName = app_conf.pop(
127                                            prefix+cls.RESULT_HANDLER_PARAMNAME, 
128                                            None)
129        if resultHandlerClassName is None:
130            resultHandler = PEPResultHandlerMiddleware
131        else:
132            resultHandler = importClass(resultHandlerClassName,
133                                    objectType=PEPResultHandlerMiddlewareBase)
134                               
135        app.add_method(resultHandler.__class__.__name__,
136                       resultHandler.filter_app_factory,
137                       global_conf,
138                       prefix=resultHandlerParamPrefix,
139                       **app_conf)
140       
141        app.add_checker(resultHandler.__class__.__name__, 
142                        Http403ForbiddenStatusHandler.intercept)
143       
144        return app
Note: See TracBrowser for help on using the repository browser.