source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/wssecurity.py @ 5063

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/wssecurity.py@5063
Revision 5063, 6.1 KB checked in by pjkersha, 12 years ago (diff)

Updated imports for new location of signature handlers in ndg.security.common.wssecurity.signaturehandler. Re-ran Java Attribute Authority client unit tests against updated Attribute Authority WSGI based service.

Line 
1"""WSGI Middleware for WS-Security
2
3Implements Digital Signature handling based around ZSI
4
5NERC Data Grid Project"""
6__author__ = "P J Kershaw"
7__date__ = "11/06/08"
8__copyright__ = "(C) 2009 Science and Technology Facilities Council"
9__license__ = "BSD - see LICENSE file in top-level directory"
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = '$Id$'
12
13import logging
14log = logging.getLogger(__name__)
15
16from ZSI.parse import ParsedSoap
17
18from ZSI.writer import SoapWriter
19from ndg.security.common.wssecurity.signaturehandler.foursuite import SignatureHandler
20from ndg.security.server.wsgi.soap import SOAPMiddleware, SOAPMiddlewareError
21
22class WSSecurityFilterError(SOAPMiddlewareError):
23    """Base exception class for WS-Security WSGI Filter"""
24    _log = log
25   
26class WSSecurityFilterConfigError(WSSecurityFilterError):
27    """WS-Security Filter Config Error"""
28 
29class WSSecurityFilter(SOAPMiddleware):
30    """Base class for WS-Security filters
31   
32    Overload pathMatch lambda so that it is more inclusive: the default is
33    for all paths to be processed by the handlers"""
34    pathMatch = lambda self, environ: \
35                        environ['PATH_INFO'].startswith(self.app_conf['path'])
36
37class SignatureFilter(WSSecurityFilter):
38    """Base class for WS-Security signature and signature verification filters
39    """
40    def __init__(self, app, app_conf, **kw):
41        super(SignatureFilter, self).__init__(app, app_conf, **kw)
42       
43        wsseCfgFilePath = self.app_conf.get('wsseCfgFilePath')       
44        wsseCfgFileSection = self.app_conf.get('wsseCfgFileSection')
45        wsseCfgFilePrefix = self.app_conf.get('wsseCfgFilePrefix')
46       
47        # Where possible remove keywords not applicable to SignatureHandler
48        kw.pop('wsseCfgFilePath', None)
49        kw.pop('wsseCfgFileSection', None)
50        kw.pop('wsseCfgFilePrefix', None)
51       
52        self.signatureHandler = SignatureHandler(cfg=wsseCfgFilePath,
53                                            cfgFileSection=wsseCfgFileSection,
54                                            cfgFilePrefix=wsseCfgFilePrefix,
55                                            **kw)
56           
57   
58class ApplySignatureFilter(SignatureFilter):
59    '''Apply WS-Security digital signature to SOAP message'''
60    def __init__(self, *arg, **kw):
61        '''Extend SignatureFilter.__init__ to enable setting of
62        WS-Security signature verification filter from config'''
63        self.wsseSignatureVerificationFilterID = kw.pop(
64                                        'wsseSignatureVerificationFilterID', 
65                                        None)
66       
67        super(ApplySignatureFilter, self).__init__(*arg, **kw)
68
69    def __call__(self, environ, start_response):
70        '''Sign message'''
71        if not self.isSOAPMessage(environ) or \
72           not self.pathMatch(environ):
73            log.debug("ApplySignatureFilter.__call__: Non-SOAP request or "
74                      "path doesn't match SOAP endpoint specified - skipping "
75                      "signature verification")
76            return self.app(environ, start_response)
77       
78        log.debug('Signing outbound message ...')
79        if self.isSOAPFaultSet(environ):
80            # TODO: If the Signature handler is signing any sub-elements in the
81            # message body this is going to run into problems because the
82            # fault content is obviously going to be different.
83            # TODO: Should SOAP faults be signed at all?
84            log.warning("Attempting to sign a SOAP fault message...")
85         
86        # The following is broken into two try blocks so that exceptions
87        # raised from the 1st can still returned as signed SOAP faults back to
88        # the client
89        try:
90            sw = self.getSOAPWriter(environ)
91           
92            # Copy signature value in order to apply signature confirmation
93            if self.signatureHandler.applySignatureConfirmation:
94                filter = environ.get(self.wsseSignatureVerificationFilterID)
95                if filter is None:
96                    raise WSSecurityFilterConfigError(
97                        'SignatureHandler "applySignatureConfirmation" flag '
98                        'is set to True but no Signature Verification Filter '
99                        'has been set in the environ: check that the '
100                        '"wsseSignatureVerificationFilterID" property is set '
101                        'and that it references the "filterID" set for the '
102                        'verification filter')
103                   
104                self.signatureHandler.b64EncSignatureValue = \
105                                filter.signatureHandler.b64EncSignatureValue
106        except Exception, e:
107            sw = self.exception2SOAPFault(environ, e)
108            self.setSOAPWriter(environ, sw)
109
110           
111        try:
112            self.signatureHandler.sign(sw)
113        except Exception, e:
114            sw = self.exception2SOAPFault(environ, e)
115            self.setSOAPWriter(environ, sw)
116       
117        return self.writeResponse(environ, start_response)
118   
119
120class SignatureVerificationFilter(SignatureFilter):
121    '''Verify WS-Security digital signature in SOAP message'''
122   
123    def __call__(self, environ, start_response):
124        '''Verify message signature'''
125        if not self.isSOAPMessage(environ) or \
126           not self.pathMatch(environ):
127            log.debug("SignatureVerificationFilter.__call__: Non-SOAP "
128                      "request or path doesn't match SOAP endpoint specified "
129                      "- skipping signature verification")
130            return self.app(environ, start_response)
131
132        log.debug("Verifying inbound message signature...")
133
134        # Add a reference to this filter in environ so that other middleware
135        # can reference it
136        self.addFilter2Environ(environ)
137       
138        try:
139            ps = self.parseRequest(environ)
140            self.signatureHandler.verify(ps)
141        except Exception, e:
142            sw = self.exception2SOAPFault(environ, e)
143            self.setSOAPWriter(environ, sw)
144           
145        return self.writeResponse(environ, start_response)
Note: See TracBrowser for help on using the repository browser.