source: TI12-security/trunk/python/Tests/etreewss/server/echoServer.py @ 4129

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/Tests/etreewss/server/echoServer.py@4129
Revision 4129, 9.1 KB checked in by cbyrom, 11 years ago (diff)

General refactoring and updating of code, including:

Removal of refC14nKw and singnedInfoC14nKw keywords in wsssecurity session manager config
(the refC14nInclNS and signedInfoC14nInclNS keywords are sufficient);
Creation of new DOM signature handler class, dom.py, based on the wsSecurity
class;
Abstraction of common code between dom.py and etree.py into new parent
class, BaseSignatureHandler?.py.
Fixing and extending use of properties in the SignatureHandler? code.
Fixing a few bugs with the original SignatureHandler? code.
Updating of test cases to new code/code structure.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2#
3# How to build an echo server using the extended code generation
4#
5import sys, os
6from ConfigParser import SafeConfigParser
7
8# Import the ZSI stuff you'd need no matter what
9from ZSI.ServiceContainer import ServiceContainer, SOAPRequestHandler, \
10                                SOAPContext, _contexts, SimpleWSResource, \
11                                ServiceInterface
12
13# This is a new method imported to show it's value
14from ZSI.ServiceContainer import GetSOAPContext
15
16from ndg.security.test.wsSecurity.server.EchoService_services_server import \
17    EchoService as _EchoService
18
19from ndg.security.common.wssecurity.etree import SignatureHandler
20from ndg.security.common.zsi_utils.elementtreeproxy import ElementTreeProxy
21
22from os.path import expandvars as xpdVars
23from os.path import join as jnPath
24mkPath = lambda file: jnPath(os.environ['NDGSEC_WSSESRV_UNITTEST_DIR'], file)
25
26
27import thread
28from ZSI.address import Address
29from ZSI.parse import ParsedSoap
30from ZSI.writer import SoapWriter
31from ZSI import ParseException, FaultFromException, FaultFromZSIException, \
32                Fault
33               
34import logging
35logging.basicConfig(level=logging.DEBUG)
36log = logging.getLogger(__name__)
37
38class ElementTreeSOAPRequestHandler(SOAPRequestHandler):
39    '''Override SOAPRequestHandler to enable use of ElementTree for parser
40    and writer'''
41   
42    def do_POST(self):
43        '''The POST command.
44        action -- SOAPAction(HTTP header) or wsa:Action(SOAP:Header)
45        '''
46        soapAction = self.headers.getheader('SOAPAction')
47        post = self.path
48        if not post:
49            raise PostNotSpecified, 'HTTP POST not specified in request'
50        if soapAction:
51            soapAction = soapAction.strip('\'"')
52        post = post.strip('\'"')
53        try:
54            ct = self.headers['content-type']
55            if ct.startswith('multipart/'):
56                cid = resolvers.MIMEResolver(ct, self.rfile)
57                xml = cid.GetSOAPPart()
58                ps = ParsedSoap(xml, 
59                                resolver=cid.Resolve,
60                                readerclass=ElementTreeProxy)
61            elif self.headers.get('transfer-encoding') == 'chunked':
62                # read content length from first line
63                hexLength = self.rfile.readline()
64                length = int(hexLength, 16)
65                xml = self.rfile.read(length)
66                ps = ParsedSoap(xml, readerclass=ElementTreeProxy)
67            else:
68                length = int(self.headers['content-length'])
69                xml = self.rfile.read(length)
70                ps = ParsedSoap(xml, readerclass=ElementTreeProxy)
71        except ParseException, e:
72            self.send_fault(FaultFromZSIException(e))
73        except Exception, e:
74            # Faulted while processing; assume it's in the header.
75            self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
76        else:
77            # Keep track of calls
78            thread_id = thread.get_ident()
79            _contexts[thread_id] = SOAPContext(self.server, xml, ps,
80                                               self.connection,
81                                               self.headers, soapAction)
82
83            try:
84                _Dispatch(ps, self.server, self.send_xml, self.send_fault, 
85                    post=post, action=soapAction)
86            except Exception, e:
87                self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
88
89            # Clean up after the call
90            if _contexts.has_key(thread_id):
91                del _contexts[thread_id]
92
93def _Dispatch(ps, server, SendResponse, SendFault, post, action, nsdict={}, 
94              **kw):
95    '''Redefine ZSI.Container._Dispatch to enable use of ElementTree for
96    SoapWriter
97    '''
98    localURL = 'http://%s:%d%s' %(server.server_name,server.server_port,post)
99    address = action
100    service = server.getNode(post)
101    isWSResource = False
102    if isinstance(service, SimpleWSResource):
103        isWSResource = True
104        service.setServiceURL(localURL)
105        address = Address()
106        try:
107            address.parse(ps)
108        except Exception, e:
109            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
110        if action and action != address.getAction():
111            e = WSActionException('SOAP Action("%s") must match WS-Action("%s") if specified.' \
112                %(action,address.getAction()))
113            return SendFault(FaultFromException(e, 0, None), **kw)
114        action = address.getAction()
115
116    if isinstance(service, ServiceInterface) is False:
117        e = NoSuchService('no service at POST(%s) in container: %s' %(post,server))
118        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
119
120    if not service.authorize(None, post, action):
121        return SendFault(Fault(Fault.Server, "Not authorized"), code=401)
122        #try:
123        #    raise NotAuthorized()
124        #except Exception, e:
125            #return SendFault(FaultFromException(e, 0, None), code=401, **kw)
126            ##return SendFault(FaultFromException(NotAuthorized(), 0, None), code=401, **kw)
127
128    try:
129        method = service.getOperation(ps, address)
130    except Exception, e:
131        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
132
133    try:
134        if isWSResource is True: 
135            result = method(ps, address)
136        else: 
137            result = method(ps)
138    except Exception, e:
139        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
140
141    # Verify if Signed
142    service.verify(ps)
143
144    # If No response just return.
145    if result is None:
146        return
147
148    sw = SoapWriter(nsdict=nsdict, outputclass=ElementTreeProxy)
149    try:
150        sw.serialize(result)
151    except Exception, e:
152        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
153
154    if isWSResource is True:
155        action = service.getResponseAction(action)
156        addressRsp = Address(action=action)
157        try:
158            addressRsp.setResponseFromWSAddress(address, localURL)
159            addressRsp.serialize(sw)
160        except Exception, e:
161            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
162
163    # Create Signatures
164    service.sign(sw)
165
166    try:
167        soapdata = str(sw)
168        return SendResponse(soapdata, **kw)
169    except Exception, e:
170        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
171
172
173class EchoService(_EchoService):
174
175    def __init__(self, **kw):
176       
177        # Stop in debugger at beginning of SOAP stub if environment variable
178        # is set
179        self.__debug = bool(os.environ.get('NDGSEC_INT_DEBUG'))
180        if self.__debug:
181            import pdb
182            pdb.set_trace()
183           
184        _EchoService.__init__(self, **kw)
185       
186    def sign(self, sw):
187        '''\
188        Overrides ServiceInterface class method to allow digital signature'''
189        self.signatureHandler.sign(sw)
190       
191    def verify(self, ps):
192        '''\
193        Overrides ServiceInterface class method to allow signature
194        verification'''
195        self.signatureHandler.verify(ps)
196       
197    def soap_Echo(self, ps, **kw):
198        '''Simple echo method to test WS-Security DSIG
199       
200        @type ps: ZSI ParsedSoap
201        @param ps: client SOAP message
202        @rtype: tuple
203        @return: response objects'''
204        if self.__debug:
205            import pdb
206            pdb.set_trace()
207       
208        response = _EchoService.soap_Echo(self, ps)   
209        response.EchoResult = "Received message from client: " + \
210                            self.request.EchoIn
211        return response
212   
213   
214    def authorize(self, auth_info, post, action):
215        '''Override default simply in order to display client request info'''
216        ctx = GetSOAPContext()
217        print "-"*80
218        print dir(ctx)
219        print "Container: ", ctx.connection
220        print "Parsed SOAP: ", ctx.parsedsoap
221        print "Container: ", ctx.container
222        print "HTTP Headers:\n", ctx.httpheaders
223        print "-"*80
224        print "Client Request:\n", ctx.xmldata
225        return 1
226
227   
228if __name__ == "__main__":
229    # Here we set up the server
230       
231    if 'NDGSEC_WSSESRV_UNITTEST_DIR' not in os.environ:
232        os.environ['NDGSEC_WSSESRV_UNITTEST_DIR'] = \
233            os.path.abspath(os.path.dirname(__file__))
234   
235    configFilePath = jnPath(os.environ['NDGSEC_WSSESRV_UNITTEST_DIR'],
236                            "echoServer.cfg")
237    cfg = SafeConfigParser()
238    cfg.read(configFilePath)
239   
240    hostname = cfg.get('setUp', 'hostname')
241    port = cfg.getint('setUp', 'port')
242    path = cfg.get('setUp', 'path')
243   
244    wsseCfgFilePath = xpdVars(cfg.get('setUp', 'wsseCfgFilePath'))
245
246    serviceContainer = ServiceContainer((hostname, port))   
247   
248    # Create the Inherited version of the server
249    echo = EchoService()
250    echo.signatureHandler = SignatureHandler(cfgFilePath=wsseCfgFilePath)
251
252    serviceContainer.setNode(echo, url=path)
253    serviceContainer.RequestHandlerClass = ElementTreeSOAPRequestHandler
254   
255    try:
256        # Run the service container
257        print "listening at http://%s:%s%s" % (hostname, port, path)
258        serviceContainer.serve_forever()
259    except KeyboardInterrupt:
260        sys.exit(0)
Note: See TracBrowser for help on using the repository browser.