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

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

Incomplete - task 2: XACML-Security Integration

  • added unit tests for XACML Context handler
  • Property svn:keywords set to Id
Line 
1"""Authorization service with SAML 2.0 authorisation decision query interface
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "17/02/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$'
11import logging
12log = logging.getLogger(__name__)
13
14from ndg.xacml.core.context.pdp import PDP
15from ndg.xacml.parsers.etree.factory import ReaderFactory as \
16    XacmlEtreePolicyReaderFactory
17
18from ndg.security.common.authz.pip.esginterface import PIP
19from ndg.security.server.xacml.ctx_handler import saml_ctx_handler
20
21
22class AuthorisationServiceMiddlewareError(Exception):
23    """Authorisation Service generic exception type"""
24   
25
26class AuthorisationServiceMiddlewareConfigError(
27                                        AuthorisationServiceMiddlewareError):
28    """Authorisation Service configuration error"""
29   
30   
31class AuthorisationServiceMiddleware(object):
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
36    '''
37    DEFAULT_PARAM_PREFIX = 'authorisationService.'
38    DEFAULT_QUERY_IFACE_KEYNAME = \
39                        'ndg.security.server.wsgi.authzservice.queryInterface'
40   
41    ENVIRON_KEYNAME_QUERY_IFACE_OPTNAME = 'queryInterfaceKeyName'
42   
43    XACML_CTX_HANDLER_PARAM_PREFIX = 'xacmlContext.'
44   
45    # For loop based assignment where possible of config options in initialise()
46    AUTHZ_SRVC_OPTION_DEFAULTS = {
47        ENVIRON_KEYNAME_QUERY_IFACE_OPTNAME: DEFAULT_QUERY_IFACE_KEYNAME,
48    }
49   
50    POLICY_FILEPATH_OPTNAME = 'policyFilePath'
51   
52    __slots__ = (
53        '__xacmlCtxHandler',
54        '__queryInterface', 
55        '__' + ENVIRON_KEYNAME_QUERY_IFACE_OPTNAME,
56        '_app',
57    )
58       
59    def __init__(self, app):
60        '''Set-up an Authorisation Service instance
61       
62        @param app: next app/middleware in WSGI stack
63        @type app: callable
64        '''
65        self._app = app
66        self.__xacmlCtxHandler = saml_ctx_handler.SamlCtxHandler()
67        self.__queryInterface = None
68        self.__queryInterfaceKeyName = None
69       
70    def initialise(self, prefix=DEFAULT_PARAM_PREFIX, **app_conf):
71        """Set-up Authorization Service middleware from keyword settings
72       
73        @type prefix: basestring
74        @param prefix: prefix for configuration items
75        @type app_conf: dict       
76        @param app_conf: PasteDeploy application specific configuration
77        dictionary
78        """
79        cls = AuthorisationServiceMiddleware
80       
81        # Loop based assignment where possible
82        for optName, default in cls.AUTHZ_SRVC_OPTION_DEFAULTS.items():
83            value = app_conf.get(prefix + optName, default)
84            setattr(self, optName, value)
85       
86        self.queryInterface = self.createQueryInterface()   
87       
88        # Initialise the Policy Information Point
89        pipCfgPrefix = prefix + cls.PIP_CFG_PREFIX
90        pip = PIP.fromConfig(app_conf, prefix=pipCfgPrefix)
91       
92        policyFilePathOptName = prefix + cls.POLICY_FILEPATH_OPTNAME
93        policyFilePath = app_conf.get(policyFilePathOptName)
94        if policyFilePath is None:
95            raise AuthorisationServiceMiddlewareConfigError("No XACML policy "
96                                                            "file set")
97           
98        # Initialise the XACML Context handler
99       
100    @classmethod
101    def filter_app_factory(cls, app, global_conf, **app_conf):
102        '''Wrapper to enable instantiation compatible with Paste Deploy
103        filter application factory function signature
104       
105        @type app: callable following WSGI interface
106        @param app: next middleware application in the chain     
107        @type global_conf: dict       
108        @param global_conf: PasteDeploy global configuration dictionary
109        @type prefix: basestring
110        @param prefix: prefix for configuration items
111        @type app_conf: dict       
112        @param app_conf: PasteDeploy application specific configuration
113        dictionary
114        '''
115        app = cls(app)
116        app.initialise(**app_conf)
117       
118        return app
119   
120    def __call__(self, environ, start_response):
121        '''Set the Authorization Decision function in environ
122       
123        @type environ: dict
124        @param environ: WSGI environment variables dictionary
125        @type start_response: function
126        @param start_response: standard WSGI start response function
127        @rtype: iterable
128        @return: next application in the WSGI stack
129        '''
130        environ[self.queryInterfaceKeyName] = self.queryInterface
131        return self._app(environ, start_response)
132
133
134    def _get_queryInterfaceKeyName(self):
135        return self.__queryInterfaceKeyName
136
137    def _set_queryInterfaceKeyName(self, val):
138        if not isinstance(val, basestring):
139            raise TypeError('Expecting %r for "getAuthzDecisionKeyName" '
140                            'attribute; got %r' % (basestring, type(val)))
141        self.__queryInterfaceKeyName = val
142       
143    queryInterfaceKeyName = property(fget=_get_queryInterfaceKeyName, 
144                                     fset=_set_queryInterfaceKeyName, 
145                                     doc="Key name used to index "
146                                         "Authorization Service SAML authz "
147                                         "decision query function in environ "
148                                         "dictionary")
149   
150    def _get_queryInterface(self):
151        return self.__queryInterface
152   
153    def _set_queryInterface(self, value):
154        if isinstance(value, basestring):
155            self.__queryInterface = importModuleObject(value)
156           
157        elif callable(value):
158            self.__queryInterface = value
159        else:
160            raise TypeError('Expecting callable for "queryInterface" '
161                            'attribute; got %r instead.' % type(value))
162   
163    queryInterface = property(_get_queryInterface,
164                              _set_queryInterface,
165                              doc="authorisation decision function set in "
166                                  "environ for downstream SAML Query "
167                                  "middleware to invoke in response to "
168                                  "<authzDecisionQuery>s")
169         
170    def createQueryInterface(self):
171        """Return the authorisation decision function so that __call__ can add
172        it to environ for the SAML Query middleware to pick up and invoke
173       
174        @return: SAML authorisation decision function
175        @rtype: callable
176        """
177       
178        # Nest function within AuthorisationServiceMiddleware method so that
179        # self is in its scope
180        def getAuthzDecision(authzDecisionQuery, samlResponse):
181            """Authorisation decision function accepts a SAML AuthzDecisionQuery
182            and calls the XACML context handler returning a response.  The
183            context handler is an interface to the the XACML Policy Decision
184            Point, XACML polic(y|ies) and Policy Information Point.
185           
186            @type authzDecisionQuery: ndg.saml.saml2.core.AuthzDecisionQuery
187            @param authzDecisionQuery: WSGI environment variables dictionary
188            @rtype: ndg.saml.saml2.core.Response
189            @return: SAML response containing Authorisation Decision Statement
190            """
191            # Create special request object which enables the context handler
192            # to formulate a response from the query and the existing response
193            # object initialised by this object
194            request = saml_ctx_handler.SamlPEPRequest()
195            request.authzDecisionQuery = authzDecisionQuery
196            request.response = samlResponse
197           
198            response = self.__xacmlCtxHandler.handlePEPRequest(request)
199           
200            return response
201           
202        return getAuthzDecision
Note: See TracBrowser for help on using the repository browser.