source: TI05-delivery/trunk/test/test_embedded.py @ 1333

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/trunk/test/test_embedded.py@1333
Revision 1333, 10.0 KB checked in by spascoe, 13 years ago (diff)

Moved python logging code into pybbftp.server

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2"""
3Tests for the bbftp daemon when embedded in Python.
4
5@author: Stephen Pascoe
6@version: $Id$
7"""
8
9import unittest
10import sys, os, signal, time, stat
11from glob import glob
12import re, tempfile, getopt
13
14HOME = os.path.abspath(os.getenv('NDG_DELIVERY_HOME', os.curdir))
15BUILDDIR = glob('%s/build/lib.*' % HOME)[0]
16BBFTP = glob('%s/src/bbftp-client*/bbftpc/bbftp' % HOME)[0]
17DATADIR = '%s/test/data' % HOME
18VERSION = open('%s/VERSION' % HOME).read()
19NDG_MESSAGE_LEN = 256
20
21NDG_HANDSHAKE = "NDG-Delivery-server %s" % VERSION
22
23sys.path.append(BUILDDIR)
24
25import pybbftp.server as server
26import traceback
27
28
29class TestAuthHandler(server.AuthHandler):
30   
31    def authenticate(self):
32        # Dump the pid so that gdm can pick it up
33        fh = open('./DS_conn.pid', 'w')
34        print >>fh, os.getpid()
35        fh.close()
36        #time.sleep(15)
37
38        msg = self.recv()
39        self.log(server.LOG_DEBUG, 'AuthContext received Auth message: %s' % msg)
40               
41        self.send(NDG_HANDSHAKE)
42
43        privatestr = self.recv()
44        self.log(server.LOG_DEBUG, "AuthContext received privatestr: %s" % privatestr)
45       
46        return self.makeAuthzHandler(msg, "TestCaseUser")
47
48    def makeAuthzHandler(self, msg, user):
49        return TestAuthzHandler(msg, "TestCaseUser")
50
51
52class TestAuthzHandler(server.LiberalAuthzHandler):
53    def __init__(self, version, username):
54        super(TestAuthzHandler, self).__init__(username)
55        self.version = version
56
57
58
59
60class TestFailAuthHandler(server.AuthHandler):
61    def authorise(self):
62        raise server.AuthenticationFailure, "TestFailAuthHandler"
63
64
65class TestPermAuthHandler(TestAuthHandler):
66    def makeAuthzHandler(self, msg, user):
67        return TestPermAuthzHandler(msg, "TestCaseUser")
68
69class TestPermAuthzHandler(TestAuthzHandler):
70    """Check's the path's other read permission and authorises accordingly.
71    """
72
73    def authzControl(self, msgcode, transferopt, path):
74        self.log(server.LOG_DEBUG, 'TestPermAuthzHandler.authzControl: msgcode = %s' % hex(msgcode))
75        return self.authzPath(path)
76
77    def authzRetr(self, path):
78        return self.authzPath(path)
79
80    def authzStore(self, path):
81        return self.authzPath(path)
82
83    def authzPath(self, path):
84        mode = stat.S_IMODE(os.stat(path)[stat.ST_MODE])
85        if (mode & 0004):
86            self.log(server.LOG_DEBUG, 'TestPermAuthzHandler OK')
87            return True
88        else:
89            self.log(server.LOG_DEBUG, 'TestPermAuthzHandler FAIL')
90            raise server.AuthorisationFailure, "TestPermAuthzHandler: no read perms"
91
92
93class BaseFixture(unittest.TestCase):
94
95    authContext = TestAuthHandler()
96
97    def setUp(self):
98        self._clearLog()
99        self._startServer()
100
101    def tearDown(self):
102        self._stopServer()
103
104    #----------------------------------------------------------------------------------
105
106
107    def _startServer(self):
108        # Start the server and store it's PID
109        self.pid = server.start(self.authContext, ['-l', 'DEBUG'])
110
111    def _stopServer(self):
112        # Stop the server process
113        os.kill(self.pid, signal.SIGTERM)
114        os.waitpid(self.pid, 0)
115
116
117    def _runClient(self, cmd, debug=False, user="testcase", privatestr=None):
118        """Run the client.
119        """
120
121        if debug:
122            f = "-d"
123        else:
124            f = "-m"
125
126        if privatestr == None:
127            p = ""
128        else:
129            p = "-P %s" % repr(privatestr)
130
131        # This is ugly but I need to redirect stderr to stdout
132        fh = os.popen('sh -c "%s %s %s -u %s -r 1 -e \'%s\' localhost" 2>&1 ' % (BBFTP, f, p, user, cmd))
133        return fh
134
135    def _clearLog(self, logfile="./bbftpd.log"):
136        os.remove(logfile)
137
138    def _readLog(self, logfile="./bbftpd.log"):
139        return open(logfile).read()
140   
141
142
143    def _findLines(self, lines, string):
144        """Look for lines matching each regular expression in lines.
145       
146        @return: True if all lines are found, else False
147        """
148       
149        for line in lines:
150            if not re.search('^%s$' % line, string, re.M):
151                return False
152           
153        return True
154
155
156    def assertLines(self, lines, string):
157        """Assert that there is a line in string matching each regular expression in lines.
158        """
159
160        for line in lines:
161            self.assert_(re.search('^%s$' % line, string, re.M))
162
163
164
165class AuthOK(BaseFixture):
166    """Test the bbftpd module.
167    """
168   
169    def testStartup(self):
170        lines = self._readLog()
171        self.assertLines(['.*Starting bbftpd'], lines)
172
173    def testDir(self):
174        """Try connecting the client and listing a directory.
175        """
176
177
178        fh = self._runClient("dir %s" % DATADIR)
179        output = fh.read()
180
181
182        self.assertLines([r'dir .*/data', r' d .*/\.', r' d .*/\.\.',
183                        r' f .*/foo', r' f .*/bar', r' f .*/baz'], output)
184
185        lines = self._readLog()
186        self.assertLines(['.*Getting new bbftp connexion.*',
187                          r'.*Authz: MSG_LIST_V2 .*/test/data/\*.*',
188                          r'.*User TestCaseUser disconnected.*'], lines)
189
190
191    def testHandshake(self):
192        """Verify handshake messages are exchanged.
193        """
194
195        fh = self._runClient("dir .", debug=True)
196        output = fh.read()
197
198        self.assertLines(['Received Auth handshake: NDG-Delivery-server %s' % VERSION], output)
199
200        lines = self._readLog()
201
202        self.assertLines(['.*AuthContext received Auth message: NDG-Delivery-client %s' % VERSION], lines)
203
204    def testPrivateStr(self):
205        """Verify the private string is sent to server.
206        """
207
208        fh = self._runClient("dir .", privatestr="testPrivateStr")
209        output = fh.read()
210
211        lines = self._readLog()
212        self.assertLines(['.*AuthContext received privatestr: testPrivateStr'], lines)
213
214    def testRetr(self):
215        """Try retrieving a file.
216        """
217
218        tmp = tempfile.mktemp('test_bbftpd')
219        fh = self._runClient("get %s/foo %s" % (DATADIR, tmp))
220
221        # Check the client output
222        output = fh.read()
223
224        self.assertLines(['get.*nogzip'], output)
225
226        # Check retrieved file
227        self.assert_(os.system('diff --brief %s/foo %s' % (DATADIR, tmp)) == 0)
228        os.remove(tmp)
229
230        # Check log
231        lines = self._readLog()
232        self.assertLines(['.*Authz: RETR .*/foo', '.*GET TestCaseUser .*/foo.*'], lines)
233
234    def testStore(self):
235        """Try storing a file.
236        """
237
238
239        src = '%s/bar' % (DATADIR)
240        dest = '%s/new_bar' % (DATADIR)
241        os.system('cp %s %s' % (src, dest))
242        fh = self._runClient("put %s %s" % (src, dest))
243
244        # Check the client output
245        output = fh.read()
246        self.assertLines(['put .* nogzip'], output)
247
248
249        # Check sent file
250        self.assert_(os.system('diff --brief %s %s' % (dest, src)) == 0)
251        os.remove(dest)
252
253        # Check log
254        lines = self._readLog()
255        self.assertLines(['.*Authz: STORE .*/new_bar', '.*PUT TestCaseUser .*/new_bar.*'], lines)
256
257
258
259class AuthFail(BaseFixture):
260    authContext = TestFailAuthHandler()
261
262    def test(self):
263        """Fail authorisation
264        """
265
266        fh = self._runClient("dir .", debug=True)
267        output = fh.read()
268
269        lines = self._readLog()
270
271        self.assertLines(['.*Error while private authentication.*'], output)
272        self.assertLines(['.*bbftpd_private_auth failed.*'], lines)
273
274
275class Authz(BaseFixture):
276    authContext = TestPermAuthHandler()
277
278    def setUp(self):
279        super(Authz, self).setUp()
280
281        # Set modes on test files
282        os.chmod('%s/bar' % (DATADIR), 0640)
283        os.chmod('%s/foo' % (DATADIR), 0655)
284
285    def testOK(self):
286        """Test dir of a readable file.
287        """
288
289        fh = self._runClient("stat %s/foo" % (DATADIR), debug=True)
290        output = fh.read()
291
292        lines = self._readLog()
293
294
295        self.assertLines(['Connection and authentication correct', 'stat /.*/foo OK'], output)
296        self.assertLines(['.*Authz: MSG_STAT .*/foo', '.*TestPermAuthzHandler OK',
297                          '.*TestPermAuthzHandler.*msgcode = %s.*' % hex(server.MSG.STAT)], lines)
298
299    def testFail(self):
300        """Test dir of a non-readable file.
301        """
302
303        fh = self._runClient("stat %s/bar" % (DATADIR), debug=True)
304        output = fh.read()
305
306        lines = self._readLog()
307
308        self.assertLines(['.* ndg_authz_control: AuthorisationFailure.* no read perms',
309                          'stat /.*/bar FAILED'], output)
310        self.assertLines(['.*Authz: MSG_STAT .*/bar', '.* TestPermAuthzHandler FAIL',
311                          '.* ndg_authz_control: AuthorisationFailure.* no read perms'], lines)
312
313
314class PythonClientAuthOK(AuthOK):
315    """Repeat AuthOK tests with a python-embedded client.
316    """
317
318    def testMultiCommand(self):
319        """Execute more than one command per connection.
320        """
321        fh = self._runClient(["dir %s" % DATADIR, "stat %s/bar" % DATADIR])
322        output = fh.read()
323
324        self.assertLines([r'dir .*/data', r' d .*/\.', r' d .*/\.\.',
325                          r' f .*/foo', r' f .*/bar', r' f .*/baz',
326                          r'stat .*'], output)
327
328        lines = self._readLog()
329        self.assertLines(['.*Getting new bbftp connexion.*',
330                          r'.*Authz: MSG_LIST_V2 .*/test/data/\*.*',
331                          r'.*User TestCaseUser disconnected.*',
332                          r'.*: Receiving MSG_STAT.*'], lines)
333
334
335
336
337    def _runClient(self, cmds, debug=False, user="testcase", privatestr=None):
338        """Run the client.
339        """
340
341        if type(cmds) == type(''):
342            cmd_args = ['-e', repr(cmds)]
343        else:
344            cmd_args = []
345            for cmd in cmds:
346                cmd_args += ['-e', repr(cmd)]
347
348        if debug:
349            f = "-d"
350        else:
351            f = "-m"
352
353        args = cmd_args + [f, '-u', user,
354                '-r', '1']
355
356        if privatestr != None:
357            args += ['-P', privatestr]
358
359        args.append('localhost')
360
361        fh = os.popen('%s/test/runclient.py %s' % (HOME, ' '.join(args)))
362
363        return fh
364
365if __name__ == '__main__':
366    unittest.main()
Note: See TracBrowser for help on using the repository browser.