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

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

Updated BaseSignatureHandler? and WSSecurityconfig classes to correctly handle config via keywords:

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