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)
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}), ])
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)
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)
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)
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 }), ])