示例#1
0
class ServerTest(unittest.TestCase):

    def setUp(self):
        os.chdir(t_path())
        self.home = 'home'
        if not os.path.isdir(self.home):
            os.mkdir(self.home)
        self.hook = TestHook()
        self.server = SFTPServer(
            SFTPServerStorage(self.home),
            hook=self.hook,
            logfile=t_path('log'),
            raise_on_error=True
        )

    def tearDown(self):
        os.chdir(t_path())
        rmtree(self.home)

    @classmethod
    def tearDownClass(cls):
        os.unlink(t_path('log'))  # comment me to see the log!
        rmtree(t_path('home'), ignore_errors=True)

    def test_init(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_INIT, sftpint(2), sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('init'), b'init hooked')

    def test_realpath(self):
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_REALPATH,
                                          sftpstring(filename))
        self.server.process()
        self.assertEqual(self.hook.get_result('realpath'), filename)
        os.unlink(filename)

    def test_stat(self):
        filename = b'services'
        with open('/etc/services') as f:
            with open(filename, 'a') as f_bis:
                f_bis.write(f.read())
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_STAT, sftpstring(filename))
        self.server.process()
        self.assertEqual(self.hook.get_result('stat'), filename)
        os.unlink(filename)

    def test_lstat(self):
        linkname = b'link'
        os.symlink('foo', linkname)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_LSTAT, sftpstring(linkname))
        self.server.process()
        self.assertEqual(self.hook.get_result('lstat'), linkname)
        os.unlink(linkname)

    def test_fstat(self):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_FSTAT, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('fstat'), filename)
        os.unlink(filename)

    def test_setstat(self):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SETSTAT,
            sftpstring(filename),
            sftpint(
                SSH2_FILEXFER_ATTR_SIZE |
                SSH2_FILEXFER_ATTR_PERMISSIONS |
                SSH2_FILEXFER_ATTR_ACMODTIME
            ),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('setstat', 'filename'), filename)
        self.assertEqual(
            pickle.loads(self.hook.get_result('setstat', 'attrs')), attrs)
        os.unlink(filename)

    def test_fsetstat(self):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSETSTAT,
            sftpstring(handle),
            sftpint(
                SSH2_FILEXFER_ATTR_SIZE |
                SSH2_FILEXFER_ATTR_PERMISSIONS |
                SSH2_FILEXFER_ATTR_ACMODTIME
            ),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('fsetstat', 'filename'),
                         filename)
        self.assertEqual(
            pickle.loads(self.hook.get_result('fsetstat', 'attrs')), attrs)
        os.unlink(filename)

    def test_opendir(self):
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        self.assertEqual(self.hook.get_result('opendir'), dirname)
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        rmtree(dirname)

    def test_readdir(self):
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_READDIR, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('readdir'), dirname)
        os.rmdir(dirname)

    def test_close(self):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('close'), filename)
        os.unlink(filename)

    def test_open(self):
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('open', 'filename'), filename)
        self.assertEqual(
            self.hook.get_result('open', 'flags'),
            self.server.get_explicit_flags(flags))
        self.assertEqual(
            pickle.loads(self.hook.get_result('open', 'attrs')),
            {b'perm': perm})
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        os.unlink(filename)

    def test_read(self):
        filename = b'services'
        read_offset = 2
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        size = (os.lstat('/etc/services').st_size)
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(chunk),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(read_offset),
            sftpint(size),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('read', 'filename'), filename)
        self.assertEqual(self.hook.get_result('read', 'offset'), read_offset)
        self.assertEqual(self.hook.get_result('read', 'size'), size)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        os.unlink(filename)

    def test_write(self):
        filename = b'services'
        write_offset = 5
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(write_offset),
            sftpstring(chunk),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('write', 'filename'), filename)
        self.assertEqual(self.hook.get_result('write', 'offset'), write_offset)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        os.unlink(filename)

    def test_mkdir(self):
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(dirname), sftpint(0))
        self.server.process()
        self.server.output_queue = b''
        self.assertEqual(self.hook.get_result('mkdir', 'filename'), dirname)
        self.assertEqual(pickle.loads(self.hook.get_result('mkdir', 'attrs')),
                         dict())
        os.rmdir(dirname)

    def test_rmdir(self):
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(dirname), sftpint(0))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_RMDIR, sftpstring(dirname))
        self.server.process()
        self.assertEqual(self.hook.get_result('rmdir'), dirname)

    def test_rm(self):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_REMOVE,
            sftpstring(filename),
            sftpint(0)
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('rm'), filename)

    def test_rename(self):
        oldpath = b'services'
        newpath = b'other_services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(oldpath),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(oldpath),
            sftpstring(newpath),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('rename', 'oldpath'), oldpath)
        self.assertEqual(self.hook.get_result('rename', 'newpath'), newpath)
        os.unlink(newpath)

    def test_symlink(self):
        linkpath = b'ugly'
        targetpath = b'ugliest'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SYMLINK, sftpstring(linkpath), sftpstring(targetpath),
            sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('symlink', 'linkpath'), linkpath)
        self.assertEqual(self.hook.get_result('symlink', 'targetpath'),
                         targetpath)

    def test_readlink(self):
        linkpath = b'ugly'
        targetpath = b'ugliest'
        os.symlink(linkpath, targetpath)
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READLINK, sftpstring(targetpath), sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('readlink'), targetpath)
示例#2
0
class ServerTest(unittest.TestCase):

    def setUp(self):
        os.chdir(t_path())
        self.home = 'home'
        if not os.path.isdir(self.home):
            os.mkdir(self.home)
        self.server = SFTPServer(
            SFTPServerStorage(self.home),
            hook=UrlRequestHook('test_url'),
            logfile=t_path('log'),
            raise_on_error=True
        )

    def tearDown(self):
        os.chdir(t_path())
        rmtree(self.home)

    @classmethod
    def tearDownClass(cls):
        os.unlink(t_path('log'))  # comment me to see the log!
        rmtree(t_path('home'), ignore_errors=True)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_init(self, mock_request):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_INIT, sftpint(2), sftpint(0))
        self.server.process()
        mock_request.assert_called_once_with(
            'POST', 'test_url/init', auth=None, data={'method': 'init'})

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_realpath(self, mock_request):
        """Additionally tests multiple urls and no path."""
        self.server.hook = UrlRequestHook(
            'test_url',
            urls_mapping={
                'realpath': ['test_url_1', 'test_url_2']},
            paths_mapping={
                'realpath': ''})
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_REALPATH,
                                          sftpstring(filename))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call(
                'POST', 'test_url_1/', auth=None,
                data={'method': 'realpath', 'filename': filename}),
            mock.call(
                'POST', 'test_url_2/', auth=None,
                data={'method': 'realpath', 'filename': filename}),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_stat(self, mock_request):
        """Additionally tests multiple urls."""
        self.server.hook = UrlRequestHook(
            'test_url',
            urls_mapping={
                'stat': ['test_url_1', 'test_url_2']})
        filename = b'services'
        with open('/etc/services') as f:
            with open(filename, 'a') as f_bis:
                f_bis.write(f.read())
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_STAT, sftpstring(filename))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call(
                'POST', 'test_url_1/stat', auth=None,
                data={'method': 'stat', 'filename': filename}),
            mock.call(
                'POST', 'test_url_2/stat', auth=None,
                data={'method': 'stat', 'filename': filename}),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_lstat(self, mock_request):
        """Additionally tests skipping mapping for different server action."""
        self.server.hook = UrlRequestHook(
            'test_url',
            urls_mapping={
                'open': ['test_url_1', 'test_url_2']})
        linkname = b'link'
        os.symlink('foo', linkname)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_LSTAT, sftpstring(linkname))
        self.server.process()
        mock_request.assert_called_once_with(
            'POST', 'test_url/lstat', auth=None,
            data={'method': 'lstat', 'filename': linkname})
        os.unlink(linkname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_fstat(self, mock_request):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_FSTAT, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call(
                'POST', 'test_url/fstat', auth=None,
                data={'method': 'fstat', 'filename': filename}),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_setstat(self, mock_request):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SETSTAT,
            sftpstring(filename),
            sftpint(
                SSH2_FILEXFER_ATTR_SIZE |
                SSH2_FILEXFER_ATTR_PERMISSIONS |
                SSH2_FILEXFER_ATTR_ACMODTIME
            ),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # write
            mock.call(
                'POST', 'test_url/setstat', auth=None,
                data={
                    'method': 'setstat', 'filename': filename,
                    'attrs': attrs}),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_fsetstat(self, mock_request):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSETSTAT,
            sftpstring(handle),
            sftpint(
                SSH2_FILEXFER_ATTR_SIZE |
                SSH2_FILEXFER_ATTR_PERMISSIONS |
                SSH2_FILEXFER_ATTR_ACMODTIME
            ),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # write
            mock.call(
                'POST', 'test_url/fsetstat', auth=None,
                data={
                    'method': 'fsetstat', 'filename': filename,
                    'attrs': attrs}),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_opendir(self, mock_request):
        """Additionally tests single url and multiple paths."""
        self.server.hook = UrlRequestHook(
            'test_url',
            paths_mapping={
                'opendir': ['test_path_1', 'test_path_2', 'test_path_3']})
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call(
                'POST', 'test_url/test_path_1', auth=None,
                data={'method': 'opendir', 'filename': dirname}),
            mock.call(
                'POST', 'test_url/test_path_2', auth=None,
                data={'method': 'opendir', 'filename': dirname}),
            mock.call(
                'POST', 'test_url/test_path_3', auth=None,
                data={'method': 'opendir', 'filename': dirname}),
            mock.ANY,  # close
        ])
        rmtree(dirname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_readdir(self, mock_request):
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_READDIR, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # opendir
            mock.call(
                'POST', 'test_url/readdir', auth=None,
                data={'method': 'readdir', 'filename': dirname}),
            mock.ANY,  # close
        ])
        os.rmdir(dirname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_close(self, mock_request):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call(
                'POST', 'test_url/close', auth=None,
                data={'method': 'close', 'filename': filename}),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_open(self, mock_request):
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call(
                'POST', 'test_url/open', auth=None,
                data={
                    'method': 'open', 'filename': filename,
                    'flags': self.server.get_explicit_flags(flags),
                    'attrs': {b'perm': perm}}),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_read(self, mock_request):
        filename = b'services'
        read_offset = 2
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        size = (os.lstat('/etc/services').st_size)
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(chunk),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(read_offset),
            sftpint(size),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # write
            mock.call(
                'POST', 'test_url/read', auth=None,
                data={
                    'method': 'read', 'filename': filename,
                    'offset': read_offset, 'size': size}),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_write(self, mock_request):
        filename = b'services'
        write_offset = 5
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(write_offset),
            sftpstring(chunk),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call(
                'POST', 'test_url/write', auth=None,
                data={
                    'method': 'write', 'filename': filename,
                    'offset': write_offset}),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_mkdir(self, mock_request):
        """Additionally tests no path."""
        self.server.hook = UrlRequestHook(
            'test_url',
            paths_mapping={
                'mkdir': ''})
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(dirname), sftpint(0))
        self.server.process()
        mock_request.assert_called_once_with(
            'POST', 'test_url/', auth=None,
            data={'method': 'mkdir', 'filename': dirname, 'attrs': dict()})
        os.rmdir(dirname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_rmdir(self, mock_request):
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(dirname), sftpint(0))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_RMDIR, sftpstring(dirname))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # mkdir
            mock.call(
                'POST', 'test_url/rmdir', auth=None,
                data={'method': 'rmdir', 'filename': dirname}),
        ])

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_rm(self, mock_request):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_REMOVE,
            sftpstring(filename),
            sftpint(0)
        )
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # close
            mock.call(
                'POST', 'test_url/rm', auth=None,
                data={'method': 'rm', 'filename': filename}),
        ])

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_rename(self, mock_request):
        oldpath = b'services'
        newpath = b'other_services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(oldpath),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(oldpath),
            sftpstring(newpath),
        )
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # close
            mock.call(
                'POST', 'test_url/rename', auth=None,
                data={
                    'method': 'rename', 'oldpath': oldpath,
                    'newpath': newpath}),
        ])
        os.unlink(newpath)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_symlink(self, mock_request):
        """Additionally tests GET method."""
        self.server.hook = UrlRequestHook('test_url', request_method='GET')
        linkpath = b'ugly'
        targetpath = b'ugliest'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SYMLINK, sftpstring(linkpath), sftpstring(targetpath),
            sftpint(0))
        self.server.process()
        mock_request.assert_called_once_with(
            'GET', 'test_url/symlink', auth=None,
            data={
                'method': 'symlink', 'linkpath': linkpath,
                'targetpath': targetpath})

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_readlink(self, mock_request):
        """Additionally tests multiple urls and multiple paths."""
        self.server.hook = UrlRequestHook(
            'test_url',
            urls_mapping={
                'readlink': ['test_url_1', 'test_url_2']},
            paths_mapping={
                'readlink': ['test_path_1', 'test_path_2']})
        linkpath = b'ugly'
        targetpath = b'ugliest'
        os.symlink(linkpath, targetpath)
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READLINK, sftpstring(targetpath), sftpint(0))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call(
                'POST', 'test_url_1/test_path_1', auth=None,
                data={'method': 'readlink', 'filename': targetpath}),
            mock.call(
                'POST', 'test_url_1/test_path_2', auth=None,
                data={'method': 'readlink', 'filename': targetpath}),
            mock.call(
                'POST', 'test_url_2/test_path_1', auth=None,
                data={'method': 'readlink', 'filename': targetpath}),
            mock.call(
                'POST', 'test_url_2/test_path_2', auth=None,
                data={'method': 'readlink', 'filename': targetpath}),
        ])
示例#3
0
class ServerTest(unittest.TestCase):
    def setUp(self):
        os.chdir(t_path())
        self.home = 'home'
        if not os.path.isdir(self.home):
            os.mkdir(self.home)
        self.hook = TestHook()
        self.server = SFTPServer(SFTPServerStorage(self.home),
                                 hook=self.hook,
                                 logfile=t_path('log'),
                                 raise_on_error=True)

    def tearDown(self):
        os.chdir(t_path())
        rmtree(self.home)

    @classmethod
    def tearDownClass(cls):
        os.unlink(t_path('log'))  # comment me to see the log!
        rmtree(t_path('home'), ignore_errors=True)

    def test_init(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_INIT, sftpint(2),
                                          sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('init'), b'init hooked')

    def test_realpath(self):
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_REALPATH,
                                          sftpstring(filename))
        self.server.process()
        self.assertEqual(self.hook.get_result('realpath'), filename)
        os.unlink(filename)

    def test_stat(self):
        filename = b'services'
        with open('/etc/services') as f:
            with open(filename, 'a') as f_bis:
                f_bis.write(f.read())
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_STAT, sftpstring(filename))
        self.server.process()
        self.assertEqual(self.hook.get_result('stat'), filename)
        os.unlink(filename)

    def test_lstat(self):
        linkname = b'link'
        os.symlink('foo', linkname)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_LSTAT, sftpstring(linkname))
        self.server.process()
        self.assertEqual(self.hook.get_result('lstat'), linkname)
        os.unlink(linkname)

    def test_fstat(self):
        filename = b'services'
        self.server.input_queue = sftpcmd(SSH2_FXP_OPEN, sftpstring(filename),
                                          sftpint(SSH2_FXF_CREAT), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_FSTAT, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('fstat'), filename)
        os.unlink(filename)

    def test_setstat(self):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SETSTAT,
            sftpstring(filename),
            sftpint(SSH2_FILEXFER_ATTR_SIZE | SSH2_FILEXFER_ATTR_PERMISSIONS
                    | SSH2_FILEXFER_ATTR_ACMODTIME),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('setstat', 'filename'), filename)
        self.assertEqual(
            pickle.loads(self.hook.get_result('setstat', 'attrs')), attrs)
        os.unlink(filename)

    def test_fsetstat(self):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSETSTAT,
            sftpstring(handle),
            sftpint(SSH2_FILEXFER_ATTR_SIZE | SSH2_FILEXFER_ATTR_PERMISSIONS
                    | SSH2_FILEXFER_ATTR_ACMODTIME),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('fsetstat', 'filename'),
                         filename)
        self.assertEqual(
            pickle.loads(self.hook.get_result('fsetstat', 'attrs')), attrs)
        os.unlink(filename)

    def test_opendir(self):
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        self.assertEqual(self.hook.get_result('opendir'), dirname)
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        rmtree(dirname)

    def test_readdir(self):
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_READDIR, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('readdir'), dirname)
        os.rmdir(dirname)

    def test_close(self):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.assertEqual(self.hook.get_result('close'), filename)
        os.unlink(filename)

    def test_open(self):
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('open', 'filename'), filename)
        self.assertEqual(self.hook.get_result('open', 'flags'), flags)
        self.assertEqual(pickle.loads(self.hook.get_result('open', 'attrs')),
                         {b'perm': perm})
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        os.unlink(filename)

    def test_read(self):
        filename = b'services'
        read_offset = 2
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        size = (os.lstat('/etc/services').st_size)
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(chunk),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(read_offset),
            sftpint(size),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('read', 'filename'), filename)
        self.assertEqual(self.hook.get_result('read', 'offset'), read_offset)
        self.assertEqual(self.hook.get_result('read', 'size'), size)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        os.unlink(filename)

    def test_write(self):
        filename = b'services'
        write_offset = 5
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(write_offset),
            sftpstring(chunk),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('write', 'filename'), filename)
        self.assertEqual(self.hook.get_result('write', 'offset'), write_offset)
        self.assertEqual(self.hook.get_result('write', 'chunk'), chunk)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        os.unlink(filename)

    def test_mkdir(self):
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(dirname),
                                          sftpint(0))
        self.server.process()
        self.server.output_queue = b''
        self.assertEqual(self.hook.get_result('mkdir', 'filename'), dirname)
        self.assertEqual(pickle.loads(self.hook.get_result('mkdir', 'attrs')),
                         dict())
        os.rmdir(dirname)

    def test_rmdir(self):
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(dirname),
                                          sftpint(0))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_RMDIR, sftpstring(dirname))
        self.server.process()
        self.assertEqual(self.hook.get_result('rmdir'), dirname)

    def test_rm(self):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_REMOVE,
                                          sftpstring(filename), sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('rm'), filename)

    def test_rename(self):
        oldpath = b'services'
        newpath = b'other_services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(oldpath),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(oldpath),
            sftpstring(newpath),
        )
        self.server.process()
        self.assertEqual(self.hook.get_result('rename', 'oldpath'), oldpath)
        self.assertEqual(self.hook.get_result('rename', 'newpath'), newpath)
        os.unlink(newpath)

    def test_symlink(self):
        linkpath = b'ugly'
        targetpath = b'ugliest'
        self.server.input_queue = sftpcmd(SSH2_FXP_SYMLINK,
                                          sftpstring(linkpath),
                                          sftpstring(targetpath), sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('symlink', 'linkpath'), linkpath)
        self.assertEqual(self.hook.get_result('symlink', 'targetpath'),
                         targetpath)

    def test_readlink(self):
        linkpath = b'ugly'
        targetpath = b'ugliest'
        os.symlink(linkpath, targetpath)
        self.server.input_queue = sftpcmd(SSH2_FXP_READLINK,
                                          sftpstring(targetpath), sftpint(0))
        self.server.process()
        self.assertEqual(self.hook.get_result('readlink'), targetpath)
示例#4
0
class ServerTest(unittest.TestCase):

    def setUp(self):
        os.chdir(t_path())
        self.home = 'home'

        if not os.path.isdir(self.home):
            os.mkdir(self.home)

        self.server = SFTPServer(
            SFTPServerVirtualChroot(self.home),
            logfile=t_path('log'),
            raise_on_error=True
        )

    def tearDown(self):
        os.chdir(t_path())
        rmtree(self.home)

    def test_mkdir(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(b'foo'), sftpint(0))
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(b'foo'), sftpint(0))
        self.assertRaises(SFTPException, self.server.process)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RMDIR, sftpstring(b'foo')
        )
        self.server.process()

        self.assertRaises(OSError, os.rmdir, 'foo')

    def test_mkdir_forbidden(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(b'../foo'), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(b'/foo'), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_open_already_existing(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle)
        )
        self.server.process()

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_EXCL),
            sftpint(0)
        )
        self.assertRaises(SFTPException, self.server.process)

        os.unlink('services')

    def test_stat(self):
        with open("/etc/services") as f:
            with open("services", 'a') as f_bis:
                f_bis.write(f.read())

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_STAT,
            sftpstring(b'services')
        )
        self.server.process()
        stat = get_sftpstat(self.server.output_queue)
        self.assertEqual(stat['size'], os.path.getsize("/etc/services"))
        self.assertEqual(stat['uid'], os.getuid())

        os.unlink('services')

    def test_lstat(self):
        os.symlink("foo", "link")

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_LSTAT,
            sftpstring(b'link')
        )
        self.server.process()
        stat = get_sftpstat(self.server.output_queue)
        self.assertEqual(stat['size'], len("foo"))
        self.assertEqual(stat['uid'], os.getuid())

        os.unlink('link')

    def test_fstat(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSTAT,
            sftpstring(handle)
        )
        self.server.process()
        stat = get_sftpstat(self.server.output_queue)
        self.assertEqual(stat['size'], 0)
        self.assertEqual(stat['uid'], os.getuid())

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle)
        )
        self.server.process()

        os.unlink('services')

    def test_setstat(self):
        atime = 1415626110
        mtime = 1415626120
        size = 10**2

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SETSTAT,
            sftpstring(b'services'),
            sftpint(
                SSH2_FILEXFER_ATTR_SIZE |
                SSH2_FILEXFER_ATTR_PERMISSIONS |
                SSH2_FILEXFER_ATTR_ACMODTIME
            ),
            sftpint64(size),  # 1000 bytes
            sftpint(33152),  # 0o100600
            sftpint(atime),
            sftpint(mtime)
        )
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle)
        )
        self.server.process()

        self.assertEqual(
            0o600,
            stat_lib.S_IMODE(os.lstat('services').st_mode)
        )

        self.assertEqual(
            atime,
            os.lstat('services').st_atime
        )

        self.assertEqual(
            mtime,
            os.lstat('services').st_mtime
        )

        self.assertEqual(
            size,
            os.lstat('services').st_size
        )

        os.unlink('services')

    def test_fsetstat(self):
        atime = 1415626110
        mtime = 1415626120
        size = 10**2

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSETSTAT,
            sftpstring(handle),
            sftpint(
                SSH2_FILEXFER_ATTR_SIZE |
                SSH2_FILEXFER_ATTR_PERMISSIONS |
                SSH2_FILEXFER_ATTR_ACMODTIME
            ),
            sftpint64(size),  # 1000 bytes
            sftpint(33152),  # 0o100600
            sftpint(atime),
            sftpint(mtime)
        )
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle)
        )
        self.server.process()

        self.assertEqual(
            0o600,
            stat_lib.S_IMODE(os.lstat('services').st_mode)
        )

        self.assertEqual(
            atime,
            os.lstat('services').st_atime
        )

        self.assertEqual(
            mtime,
            os.lstat('services').st_mtime
        )

        self.assertEqual(
            size,
            os.lstat('services').st_size
        )

        os.unlink('services')

    def test_open_forbidden(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(
                b'/etc/services'), sftpint(SSH2_FXF_CREAT), sftpint(0)
        )
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(
                b'../../foo'), sftpint(SSH2_FXF_CREAT), sftpint(0)
        )
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_read_subdir(self):
        f = {b'.', b'..', b'bar'}  # files inside foo
        os.mkdir("foo")
        foobar_path = os.path.join("foo", "bar")
        with open(foobar_path, 'a') as stream:
            print("foobar", file=stream)
        # bar_size = os.lstat(foobar_path).st_size

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPENDIR,
            sftpstring(b'foo')
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        l = set()
        while (True):
            # reset output queue
            self.server.output_queue = b''
            self.server.input_queue = sftpcmd(
                SSH2_FXP_READDIR,
                sftpstring(handle),
            )
            try:
                self.server.process()
                filename = get_sftpname(self.server.output_queue)
                l.add(filename)
            except:
                break
        self.assertEqual(l, f)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

        rmtree("foo")

    def test_remove(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle)
        )
        self.server.process()

        self.server.input_queue = sftpcmd(
            SSH2_FXP_REMOVE,
            sftpstring(b'services'),
            sftpint(0)
        )
        self.server.process()

    def test_rename(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(b'services'),
            sftpstring(b'other_services'),
        )
        self.server.process()
        self.assertIn('other_services', os.listdir('.'))

    def test_remove_notfound(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_REMOVE,
            sftpstring(b'services'),
            sftpint(0)
        )
        self.assertRaises(SFTPNotFound, self.server.process)

    def test_remove_forbidden(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_REMOVE,
            sftpstring(b'/etc/services'),
            sftpint(0)
        )
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_rename_forbidden(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(b'services'),
            sftpstring(b'/etc/other_services'),
        )
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(b'/etc/services'),
            sftpstring(b'/etc/other_services'),
        )
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_mkdir_notfound(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_MKDIR, sftpstring(b'bad/ugly'), sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

    def test_readdir(self):
        f = {b'.', b'..', b'foo', b'bar'}
        os.mkdir("foo")
        os.close(os.open("bar", os.O_CREAT))

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPENDIR,
            sftpstring(b'.')
        )
        self.server.process()

        handle = get_sftphandle(self.server.output_queue)

        l = set()
        while (True):
            # reset output queue
            self.server.output_queue = b''
            self.server.input_queue = sftpcmd(
                SSH2_FXP_READDIR,
                sftpstring(handle),
            )
            try:
                self.server.process()
                filename = get_sftpname(self.server.output_queue)
                l.add(filename)
            except:
                break
        self.assertEqual(l, f)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

        os.unlink("bar")
        os.rmdir("foo")

    def test_symlink(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SYMLINK, sftpstring(b'bad/ugly'),
            sftpstring(b'bad/ugliest'), sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_SYMLINK, sftpstring(b'/bad/ugly'),
            sftpstring(b'bad/ugliest'), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_SYMLINK, sftpstring(b'bad/ugly'),
            sftpstring(b'/bad/ugliest'), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_SYMLINK, sftpstring(b'ugly'),
            sftpstring(b'ugliest'), sftpint(0))
        self.server.process()
        self.assertIn('ugly', os.listdir('.'))

    def test_readlink(self):
        os.symlink("infound", "foo")

        self.server.input_queue = sftpcmd(
            SSH2_FXP_READLINK, sftpstring(b'foo'), sftpint(0))
        self.server.process()
        link = get_sftpname(self.server.output_queue)
        self.assertEqual(link, b"infound")

    def test_readdir_broken_symlink(self):
        os.symlink("infound", "foo")

        self.server.input_queue = sftpcmd(
            SSH2_FXP_READLINK, sftpstring(b'foo'), sftpint(0))
        self.server.process()
        link = get_sftpname(self.server.output_queue)
        self.assertEqual(link, b"infound")
        self.server.output_queue = b''

        f = {b'.', b'..', b'foo'}
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPENDIR,
            sftpstring(b'.')
        )
        self.server.process()

        handle = get_sftphandle(self.server.output_queue)

        l = set()
        while (True):
            # reset output queue
            self.server.output_queue = b''
            self.server.input_queue = sftpcmd(
                SSH2_FXP_READDIR,
                sftpstring(handle),
            )
            try:
                self.server.process()
                filename = get_sftpname(self.server.output_queue)
                l.add(filename)
            except:
                break
        self.assertEqual(l, f)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

    def test_init(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_INIT, sftpint(2), sftpint(0)
        )
        self.server.process()
        version = get_sftpint(self.server.output_queue)
        self.assertEqual(version, SSH2_FILEXFER_VERSION)

    def test_rmdir_notfound(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RMDIR, sftpstring(b'bad/ugly'), sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

    def test_copy_services(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644)
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        etc_services_size = \
            os.lstat('/etc/services').st_size  # size of the whole file
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(etc_services)
        )
        self.server.process()

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(0),
            sftpint(
                etc_services_size
            )
        )
        self.server.process()
        data = get_sftpdata(self.server.output_queue)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(etc_services_size),
            sftpint(1)  # wait for the EOF
        )
        # EOF status is raised as an exception
        self.assertRaises(SFTPException, self.server.process)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle)
        )
        self.server.process()

        self.assertEqual(
            etc_services,
            open('services', 'rb').read()
        )
        self.assertEqual(
            etc_services,
            data
        )
        self.assertEqual(
            0o644,
            stat_lib.S_IMODE(os.lstat('services').st_mode)
        )
        self.assertEqual(
            etc_services_size,
            os.lstat('services').st_size
        )

        os.unlink('services')

    @classmethod
    def tearDownClass(cls):
        os.unlink(t_path("log"))  # comment me to see the log!
        rmtree(t_path("home"), ignore_errors=True)
示例#5
0
class ServerTest(unittest.TestCase):
    def setUp(self):
        os.chdir(t_path())
        self.home = 'home'

        if not os.path.isdir(self.home):
            os.mkdir(self.home)

        self.server = SFTPServer(SFTPServerVirtualChroot(self.home),
                                 logfile=t_path('log'),
                                 raise_on_error=True)

    def tearDown(self):
        os.chdir(t_path())
        rmtree(self.home)

    def test_mkdir(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(b'foo'),
                                          sftpint(0))
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(b'foo'),
                                          sftpint(0))
        self.assertRaises(SFTPException, self.server.process)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_RMDIR, sftpstring(b'foo'))
        self.server.process()

        self.assertRaises(OSError, os.rmdir, 'foo')

    def test_mkdir_forbidden(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR,
                                          sftpstring(b'../foo'), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(b'/foo'),
                                          sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_open_already_existing(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_OPEN,
                                          sftpstring(b'services'),
                                          sftpint(SSH2_FXF_CREAT), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_EXCL), sftpint(0))
        self.assertRaises(SFTPException, self.server.process)

        os.unlink('services')

    def test_stat(self):
        with open("/etc/services") as f:
            with open("services", 'a') as f_bis:
                f_bis.write(f.read())

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_STAT,
                                          sftpstring(b'services'))
        self.server.process()
        stat = get_sftpstat(self.server.output_queue)
        self.assertEqual(stat['size'], os.path.getsize("/etc/services"))
        self.assertEqual(stat['uid'], os.getuid())

        os.unlink('services')

    def test_lstat(self):
        os.symlink("foo", "link")

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_LSTAT, sftpstring(b'link'))
        self.server.process()
        stat = get_sftpstat(self.server.output_queue)
        self.assertEqual(stat['size'], len("foo"))
        self.assertEqual(stat['uid'], os.getuid())

        os.unlink('link')

    def test_fstat(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_OPEN,
                                          sftpstring(b'services'),
                                          sftpint(SSH2_FXF_CREAT), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_FSTAT, sftpstring(handle))
        self.server.process()
        stat = get_sftpstat(self.server.output_queue)
        self.assertEqual(stat['size'], 0)
        self.assertEqual(stat['uid'], os.getuid())

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()

        os.unlink('services')

    def test_setstat(self):
        atime = 1415626110
        mtime = 1415626120
        size = 10**2

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SETSTAT,
            sftpstring(b'services'),
            sftpint(SSH2_FILEXFER_ATTR_SIZE | SSH2_FILEXFER_ATTR_PERMISSIONS
                    | SSH2_FILEXFER_ATTR_ACMODTIME),
            sftpint64(size),  # 1000 bytes
            sftpint(33152),  # 0o100600
            sftpint(atime),
            sftpint(mtime))
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()

        self.assertEqual(0o600, stat_lib.S_IMODE(os.lstat('services').st_mode))

        self.assertEqual(atime, os.lstat('services').st_atime)

        self.assertEqual(mtime, os.lstat('services').st_mtime)

        self.assertEqual(size, os.lstat('services').st_size)

        os.unlink('services')

    def test_fsetstat(self):
        atime = 1415626110
        mtime = 1415626120
        size = 10**2

        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSETSTAT,
            sftpstring(handle),
            sftpint(SSH2_FILEXFER_ATTR_SIZE | SSH2_FILEXFER_ATTR_PERMISSIONS
                    | SSH2_FILEXFER_ATTR_ACMODTIME),
            sftpint64(size),  # 1000 bytes
            sftpint(33152),  # 0o100600
            sftpint(atime),
            sftpint(mtime))
        self.server.process()

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()

        self.assertEqual(0o600, stat_lib.S_IMODE(os.lstat('services').st_mode))

        self.assertEqual(atime, os.lstat('services').st_atime)

        self.assertEqual(mtime, os.lstat('services').st_mtime)

        self.assertEqual(size, os.lstat('services').st_size)

        os.unlink('services')

    def test_open_forbidden(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_OPEN,
                                          sftpstring(b'/etc/services'),
                                          sftpint(SSH2_FXF_CREAT), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(SSH2_FXP_OPEN,
                                          sftpstring(b'../../foo'),
                                          sftpint(SSH2_FXF_CREAT), sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_read_subdir(self):
        f = {b'.', b'..', b'bar'}  # files inside foo
        os.mkdir("foo")
        foobar_path = os.path.join("foo", "bar")
        with open(foobar_path, 'a') as stream:
            print("foobar", file=stream)
        # bar_size = os.lstat(foobar_path).st_size

        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR, sftpstring(b'foo'))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        l = set()
        while (True):
            # reset output queue
            self.server.output_queue = b''
            self.server.input_queue = sftpcmd(
                SSH2_FXP_READDIR,
                sftpstring(handle),
            )
            try:
                self.server.process()
                filename = get_sftpname(self.server.output_queue)
                l.add(filename)
            except:
                break
        self.assertEqual(l, f)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

        rmtree("foo")

    def test_remove(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()

        self.server.input_queue = sftpcmd(SSH2_FXP_REMOVE,
                                          sftpstring(b'services'), sftpint(0))
        self.server.process()

    def test_rename(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(b'services'),
            sftpstring(b'other_services'),
        )
        self.server.process()
        self.assertIn('other_services', os.listdir('.'))

    def test_remove_notfound(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_REMOVE,
                                          sftpstring(b'services'), sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

    def test_remove_forbidden(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_REMOVE,
                                          sftpstring(b'/etc/services'),
                                          sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_rename_forbidden(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(b'services'),
            sftpstring(b'/etc/other_services'),
        )
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(b'/etc/services'),
            sftpstring(b'/etc/other_services'),
        )
        self.assertRaises(SFTPForbidden, self.server.process)

    def test_mkdir_notfound(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR,
                                          sftpstring(b'bad/ugly'), sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

    def test_readdir(self):
        f = {b'.', b'..', b'foo', b'bar'}
        os.mkdir("foo")
        os.close(os.open("bar", os.O_CREAT))

        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR, sftpstring(b'.'))
        self.server.process()

        handle = get_sftphandle(self.server.output_queue)

        l = set()
        while (True):
            # reset output queue
            self.server.output_queue = b''
            self.server.input_queue = sftpcmd(
                SSH2_FXP_READDIR,
                sftpstring(handle),
            )
            try:
                self.server.process()
                filename = get_sftpname(self.server.output_queue)
                l.add(filename)
            except:
                break
        self.assertEqual(l, f)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

        os.unlink("bar")
        os.rmdir("foo")

    def test_symlink(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_SYMLINK,
                                          sftpstring(b'bad/ugly'),
                                          sftpstring(b'bad/ugliest'),
                                          sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

        self.server.input_queue = sftpcmd(SSH2_FXP_SYMLINK,
                                          sftpstring(b'/bad/ugly'),
                                          sftpstring(b'bad/ugliest'),
                                          sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(SSH2_FXP_SYMLINK,
                                          sftpstring(b'bad/ugly'),
                                          sftpstring(b'/bad/ugliest'),
                                          sftpint(0))
        self.assertRaises(SFTPForbidden, self.server.process)

        self.server.input_queue = sftpcmd(SSH2_FXP_SYMLINK,
                                          sftpstring(b'ugly'),
                                          sftpstring(b'ugliest'), sftpint(0))
        self.server.process()
        self.assertIn('ugly', os.listdir('.'))

    def test_readlink(self):
        os.symlink("infound", "foo")

        self.server.input_queue = sftpcmd(SSH2_FXP_READLINK,
                                          sftpstring(b'foo'), sftpint(0))
        self.server.process()
        link = get_sftpname(self.server.output_queue)
        self.assertEqual(link, b"infound")

    def test_readdir_broken_symlink(self):
        os.symlink("infound", "foo")

        self.server.input_queue = sftpcmd(SSH2_FXP_READLINK,
                                          sftpstring(b'foo'), sftpint(0))
        self.server.process()
        link = get_sftpname(self.server.output_queue)
        self.assertEqual(link, b"infound")
        self.server.output_queue = b''

        f = {b'.', b'..', b'foo'}
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR, sftpstring(b'.'))
        self.server.process()

        handle = get_sftphandle(self.server.output_queue)

        l = set()
        while (True):
            # reset output queue
            self.server.output_queue = b''
            self.server.input_queue = sftpcmd(
                SSH2_FXP_READDIR,
                sftpstring(handle),
            )
            try:
                self.server.process()
                filename = get_sftpname(self.server.output_queue)
                l.add(filename)
            except:
                break
        self.assertEqual(l, f)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_CLOSE,
            sftpstring(handle),
        )
        self.server.process()

    def test_init(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_INIT, sftpint(2),
                                          sftpint(0))
        self.server.process()
        version = get_sftpint(self.server.output_queue)
        self.assertEqual(version, SSH2_FILEXFER_VERSION)

    def test_rmdir_notfound(self):
        self.server.input_queue = sftpcmd(SSH2_FXP_RMDIR,
                                          sftpstring(b'bad/ugly'), sftpint(0))
        self.assertRaises(SFTPNotFound, self.server.process)

    def test_copy_services(self):
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(b'services'),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)

        # reset output queue
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        etc_services_size = \
            os.lstat('/etc/services').st_size  # size of the whole file
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_READ, sftpstring(handle),
                                          sftpint64(0),
                                          sftpint(etc_services_size))
        self.server.process()
        data = get_sftpdata(self.server.output_queue)

        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(etc_services_size),
            sftpint(1)  # wait for the EOF
        )
        # EOF status is raised as an exception
        self.assertRaises(SFTPException, self.server.process)

        # reset output queue
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()

        self.assertEqual(etc_services, open('services', 'rb').read())
        self.assertEqual(etc_services, data)
        self.assertEqual(0o644, stat_lib.S_IMODE(os.lstat('services').st_mode))
        self.assertEqual(etc_services_size, os.lstat('services').st_size)

        os.unlink('services')

    @classmethod
    def tearDownClass(cls):
        os.unlink(t_path("log"))  # comment me to see the log!
        rmtree(t_path("home"), ignore_errors=True)
示例#6
0
class ServerTest(unittest.TestCase):
    def setUp(self):
        os.chdir(t_path())
        self.home = 'home'
        if not os.path.isdir(self.home):
            os.mkdir(self.home)
        self.server = SFTPServer(SFTPServerStorage(self.home),
                                 hook=UrlRequestHook('test_url'),
                                 logfile=t_path('log'),
                                 raise_on_error=True)

    def tearDown(self):
        os.chdir(t_path())
        rmtree(self.home)

    @classmethod
    def tearDownClass(cls):
        os.unlink(t_path('log'))  # comment me to see the log!
        rmtree(t_path('home'), ignore_errors=True)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_init(self, mock_request):
        self.server.input_queue = sftpcmd(SSH2_FXP_INIT, sftpint(2),
                                          sftpint(0))
        self.server.process()
        mock_request.assert_called_once_with('POST',
                                             'test_url/init',
                                             auth=None,
                                             data={'method': 'init'})

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_realpath(self, mock_request):
        """Additionally tests multiple urls and no path."""
        self.server.hook = UrlRequestHook(
            'test_url',
            urls_mapping={'realpath': ['test_url_1', 'test_url_2']},
            paths_mapping={'realpath': ''})
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_REALPATH,
                                          sftpstring(filename))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call('POST',
                      'test_url_1/',
                      auth=None,
                      data={
                          'method': 'realpath',
                          'filename': filename
                      }),
            mock.call('POST',
                      'test_url_2/',
                      auth=None,
                      data={
                          'method': 'realpath',
                          'filename': filename
                      }),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_stat(self, mock_request):
        """Additionally tests multiple urls."""
        self.server.hook = UrlRequestHook(
            'test_url', urls_mapping={'stat': ['test_url_1', 'test_url_2']})
        filename = b'services'
        with open('/etc/services') as f:
            with open(filename, 'a') as f_bis:
                f_bis.write(f.read())
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_STAT, sftpstring(filename))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call('POST',
                      'test_url_1/stat',
                      auth=None,
                      data={
                          'method': 'stat',
                          'filename': filename
                      }),
            mock.call('POST',
                      'test_url_2/stat',
                      auth=None,
                      data={
                          'method': 'stat',
                          'filename': filename
                      }),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_lstat(self, mock_request):
        """Additionally tests skipping mapping for different server action."""
        self.server.hook = UrlRequestHook(
            'test_url', urls_mapping={'open': ['test_url_1', 'test_url_2']})
        linkname = b'link'
        os.symlink('foo', linkname)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_LSTAT, sftpstring(linkname))
        self.server.process()
        mock_request.assert_called_once_with('POST',
                                             'test_url/lstat',
                                             auth=None,
                                             data={
                                                 'method': 'lstat',
                                                 'filename': linkname
                                             })
        os.unlink(linkname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_fstat(self, mock_request):
        filename = b'services'
        self.server.input_queue = sftpcmd(SSH2_FXP_OPEN, sftpstring(filename),
                                          sftpint(SSH2_FXF_CREAT), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_FSTAT, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call('POST',
                      'test_url/fstat',
                      auth=None,
                      data={
                          'method': 'fstat',
                          'filename': filename
                      }),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_setstat(self, mock_request):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_SETSTAT,
            sftpstring(filename),
            sftpint(SSH2_FILEXFER_ATTR_SIZE | SSH2_FILEXFER_ATTR_PERMISSIONS
                    | SSH2_FILEXFER_ATTR_ACMODTIME),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # write
            mock.call('POST',
                      'test_url/setstat',
                      auth=None,
                      data={
                          'method': 'setstat',
                          'filename': filename,
                          'attrs': attrs
                      }),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_fsetstat(self, mock_request):
        filename = b'services'
        attrs = {
            b'size': 10**2,
            b'perm': 0o100600,
            b'atime': 1415626110,
            b'mtime': 1415626120,
        }
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE), sftpint(0))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        etc_services = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(SSH2_FXP_WRITE, sftpstring(handle),
                                          sftpint64(0),
                                          sftpstring(etc_services))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_FSETSTAT,
            sftpstring(handle),
            sftpint(SSH2_FILEXFER_ATTR_SIZE | SSH2_FILEXFER_ATTR_PERMISSIONS
                    | SSH2_FILEXFER_ATTR_ACMODTIME),
            sftpint64(attrs[b'size']),
            sftpint(attrs[b'perm']),
            sftpint(attrs[b'atime']),
            sftpint(attrs[b'mtime']),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # write
            mock.call('POST',
                      'test_url/fsetstat',
                      auth=None,
                      data={
                          'method': 'fsetstat',
                          'filename': filename,
                          'attrs': attrs
                      }),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_opendir(self, mock_request):
        """Additionally tests single url and multiple paths."""
        self.server.hook = UrlRequestHook(
            'test_url',
            paths_mapping={
                'opendir': ['test_path_1', 'test_path_2', 'test_path_3']
            })
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call('POST',
                      'test_url/test_path_1',
                      auth=None,
                      data={
                          'method': 'opendir',
                          'filename': dirname
                      }),
            mock.call('POST',
                      'test_url/test_path_2',
                      auth=None,
                      data={
                          'method': 'opendir',
                          'filename': dirname
                      }),
            mock.call('POST',
                      'test_url/test_path_3',
                      auth=None,
                      data={
                          'method': 'opendir',
                          'filename': dirname
                      }),
            mock.ANY,  # close
        ])
        rmtree(dirname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_readdir(self, mock_request):
        dirname = b'foo'
        os.mkdir(dirname)
        self.server.input_queue = sftpcmd(SSH2_FXP_OPENDIR,
                                          sftpstring(dirname))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_READDIR, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # opendir
            mock.call('POST',
                      'test_url/readdir',
                      auth=None,
                      data={
                          'method': 'readdir',
                          'filename': dirname
                      }),
            mock.ANY,  # close
        ])
        os.rmdir(dirname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_close(self, mock_request):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(0),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call('POST',
                      'test_url/close',
                      auth=None,
                      data={
                          'method': 'close',
                          'filename': filename
                      }),
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_open(self, mock_request):
        filename = b'services'
        flags = SSH2_FXF_CREAT | SSH2_FXF_WRITE
        perm = 0o100600
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(flags),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(perm),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call('POST',
                      'test_url/open',
                      auth=None,
                      data={
                          'method': 'open',
                          'filename': filename,
                          'flags': self.server.get_explicit_flags(flags),
                          'attrs': {
                              b'perm': perm
                          }
                      }),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_read(self, mock_request):
        filename = b'services'
        read_offset = 2
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        size = (os.lstat('/etc/services').st_size)
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(0),
            sftpstring(chunk),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_READ,
            sftpstring(handle),
            sftpint64(read_offset),
            sftpint(size),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # write
            mock.call('POST',
                      'test_url/read',
                      auth=None,
                      data={
                          'method': 'read',
                          'filename': filename,
                          'offset': read_offset,
                          'size': size
                      }),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_write(self, mock_request):
        filename = b'services'
        write_offset = 5
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN,
            sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE | SSH2_FXF_READ),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS),
            sftpint(0o644),
        )
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        chunk = open('/etc/services', 'rb').read()
        self.server.input_queue = sftpcmd(
            SSH2_FXP_WRITE,
            sftpstring(handle),
            sftpint64(write_offset),
            sftpstring(chunk),
        )
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.call('POST',
                      'test_url/write',
                      auth=None,
                      data={
                          'method': 'write',
                          'filename': filename,
                          'offset': write_offset
                      }),
            mock.ANY,  # close
        ])
        os.unlink(filename)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_mkdir(self, mock_request):
        """Additionally tests no path."""
        self.server.hook = UrlRequestHook('test_url',
                                          paths_mapping={'mkdir': ''})
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(dirname),
                                          sftpint(0))
        self.server.process()
        mock_request.assert_called_once_with('POST',
                                             'test_url/',
                                             auth=None,
                                             data={
                                                 'method': 'mkdir',
                                                 'filename': dirname,
                                                 'attrs': dict()
                                             })
        os.rmdir(dirname)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_rmdir(self, mock_request):
        dirname = b'foo'
        # sftpint(0) means no attrs
        self.server.input_queue = sftpcmd(SSH2_FXP_MKDIR, sftpstring(dirname),
                                          sftpint(0))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_RMDIR, sftpstring(dirname))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # mkdir
            mock.call('POST',
                      'test_url/rmdir',
                      auth=None,
                      data={
                          'method': 'rmdir',
                          'filename': dirname
                      }),
        ])

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_rm(self, mock_request):
        filename = b'services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(filename),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_REMOVE,
                                          sftpstring(filename), sftpint(0))
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # close
            mock.call('POST',
                      'test_url/rm',
                      auth=None,
                      data={
                          'method': 'rm',
                          'filename': filename
                      }),
        ])

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_rename(self, mock_request):
        oldpath = b'services'
        newpath = b'other_services'
        self.server.input_queue = sftpcmd(
            SSH2_FXP_OPEN, sftpstring(oldpath),
            sftpint(SSH2_FXF_CREAT | SSH2_FXF_WRITE),
            sftpint(SSH2_FILEXFER_ATTR_PERMISSIONS), sftpint(0o644))
        self.server.process()
        handle = get_sftphandle(self.server.output_queue)
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(SSH2_FXP_CLOSE, sftpstring(handle))
        self.server.process()
        self.server.output_queue = b''
        self.server.input_queue = sftpcmd(
            SSH2_FXP_RENAME,
            sftpstring(oldpath),
            sftpstring(newpath),
        )
        self.server.process()
        mock_request.assert_has_calls([
            mock.ANY,  # open
            mock.ANY,  # close
            mock.call('POST',
                      'test_url/rename',
                      auth=None,
                      data={
                          'method': 'rename',
                          'oldpath': oldpath,
                          'newpath': newpath
                      }),
        ])
        os.unlink(newpath)

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_symlink(self, mock_request):
        """Additionally tests GET method."""
        self.server.hook = UrlRequestHook('test_url', request_method='GET')
        linkpath = b'ugly'
        targetpath = b'ugliest'
        self.server.input_queue = sftpcmd(SSH2_FXP_SYMLINK,
                                          sftpstring(linkpath),
                                          sftpstring(targetpath), sftpint(0))
        self.server.process()
        mock_request.assert_called_once_with('GET',
                                             'test_url/symlink',
                                             auth=None,
                                             data={
                                                 'method': 'symlink',
                                                 'linkpath': linkpath,
                                                 'targetpath': targetpath
                                             })

    @mock.patch('pysftpserver.urlrequesthook.request')
    def test_readlink(self, mock_request):
        """Additionally tests multiple urls and multiple paths."""
        self.server.hook = UrlRequestHook(
            'test_url',
            urls_mapping={'readlink': ['test_url_1', 'test_url_2']},
            paths_mapping={'readlink': ['test_path_1', 'test_path_2']})
        linkpath = b'ugly'
        targetpath = b'ugliest'
        os.symlink(linkpath, targetpath)
        self.server.input_queue = sftpcmd(SSH2_FXP_READLINK,
                                          sftpstring(targetpath), sftpint(0))
        self.server.process()
        mock_request.assert_has_calls([
            mock.call('POST',
                      'test_url_1/test_path_1',
                      auth=None,
                      data={
                          'method': 'readlink',
                          'filename': targetpath
                      }),
            mock.call('POST',
                      'test_url_1/test_path_2',
                      auth=None,
                      data={
                          'method': 'readlink',
                          'filename': targetpath
                      }),
            mock.call('POST',
                      'test_url_2/test_path_1',
                      auth=None,
                      data={
                          'method': 'readlink',
                          'filename': targetpath
                      }),
            mock.call('POST',
                      'test_url_2/test_path_2',
                      auth=None,
                      data={
                          'method': 'readlink',
                          'filename': targetpath
                      }),
        ])