def __init__(self, root_fs, cmd_channel): """ - (str) root: the user "real" home directory (e.g. '/home/user') - (instance) cmd_channel: the FTPHandler class instance """ # Set initial current working directory. # By default initial cwd is set to "/" to emulate a chroot jail. # If a different behavior is desired (e.g. initial cwd = root, # to reflect the real filesystem) users overriding this class # are responsible to set _cwd attribute as necessary. self._cwd = u('/') self._fs = fs.open_fs(root_fs) i = self._fs.getinfo( '/', namespaces=['stat', 'lstat', 'details', 'access', 'link']) self._has_access_info = i.has_namespace('access') self._has_link_info = i.has_namespace('access') self._has_stat_info = i.has_namespace('stat') self._has_lstat_info = i.has_namespace('lstat') if self._has_link_info: def readlink(self, path): i = self.getinfo(path) return i.target #setattr(self, "readlink", readlink) self.readlink = MethodType(readlink, self) else: self.readlink = None self._root = u('/') #self._fs.root_path self.cmd_channel = cmd_channel
def test_validpath_validlink(self): # Test validpath by issuing a symlink pointing to a path # inside the root directory. testfn = self.get_testfn() testfn2 = self.get_testfn() fs = AbstractedFS(u('/'), None) fs._root = HOME touch(testfn) os.symlink(testfn, testfn2) self.assertTrue(fs.validpath(u(testfn)))
def test_validpath_validlink(self): # Test validpath by issuing a symlink pointing to a path # inside the root directory. fs = AbstractedFS(u('/'), None) fs._root = HOME TESTFN2 = TESTFN + '1' try: touch(TESTFN) os.symlink(TESTFN, TESTFN2) self.assertTrue(fs.validpath(u(TESTFN))) finally: safe_remove(TESTFN, TESTFN2)
def test_validpath_external_symlink(self): # Test validpath by issuing a symlink pointing to a path # outside the root directory. fs = AbstractedFS(u('/'), None) fs._root = HOME # tempfile should create our file in /tmp directory # which should be outside the user root. If it is # not we just skip the test. with tempfile.NamedTemporaryFile() as file: try: if HOME == os.path.dirname(file.name): return os.symlink(file.name, TESTFN) self.assertFalse(fs.validpath(u(TESTFN))) finally: safe_remove(TESTFN)
def ftpnorm(self, ftppath): """Normalize a "virtual" ftp pathname (typically the raw string coming from client) depending on the current working directory. Example (having "/foo" as current working directory): >>> ftpnorm('bar') '/foo/bar' Note: directory separators are system independent ("/"). Pathname returned is always absolutized. """ assert isinstance(ftppath, unicode), ftppath if os.path.isabs(ftppath): p = os.path.normpath(ftppath) else: p = os.path.normpath(os.path.join(self.cwd, ftppath)) # normalize string in a standard web-path notation having '/' # as separator. if os.sep == "\\": p = p.replace("\\", "/") # os.path.normpath supports UNC paths (e.g. "//a/b/c") but we # don't need them. In case we get an UNC path we collapse # redundant separators appearing at the beginning of the string while p[:2] == '//': p = p[1:] # Anti path traversal: don't trust user input, in the event # that self.cwd is not absolute, return "/" as a safety measure. # This is for extra protection, maybe not really necessary. if not os.path.isabs(p): p = u("/") return p
def fs2ftp(self, fspath): """Translate a "real" filesystem pathname into equivalent absolute "virtual" ftp pathname depending on the user's root directory. Example (having "/home/user" as root directory): >>> fs2ftp("/home/user/foo") '/foo' As for ftpnorm, directory separators are system independent ("/") and pathname returned is always absolutized. On invalid pathnames escaping from user's root directory (e.g. "/home" when root is "/home/user") always return "/". """ assert isinstance(fspath, unicode), fspath if os.path.isabs(fspath): p = os.path.normpath(fspath) else: p = os.path.normpath(os.path.join(self.root, fspath)) if not self.validpath(p): return u('/') p = p.replace(os.sep, "/") p = p[len(self.root):] if not p.startswith('/'): p = '/' + p return p
def test_validpath(self): # Tests for validpath method. fs = AbstractedFS(u('/'), None) fs._root = HOME self.assertTrue(fs.validpath(HOME)) self.assertTrue(fs.validpath(HOME + '/')) self.assertFalse(fs.validpath(HOME + 'bar'))
def test_case(self): root = getcwdu() fs = UnixFilesystem(root, None) self.assertEqual(fs.root, root) self.assertEqual(fs.cwd, root) cdup = os.path.dirname(root) self.assertEqual(fs.ftp2fs(u('..')), cdup) self.assertEqual(fs.fs2ftp(root), root)
def remove_test_files(): """Remove files and directores created during tests.""" for name in os.listdir(u('.')): if name.startswith(tempfile.template): if os.path.isdir(name): shutil.rmtree(name) else: safe_remove(name)
def __init__(self, root, cmd_channel): """ - (str) root: the user "real" home directory (e.g. '/home/user') - (instance) cmd_channel: the FTPHandler class instance """ assert isinstance(root, unicode) # Set initial current working directory. # By default initial cwd is set to "/" to emulate a chroot jail. # If a different behavior is desired (e.g. initial cwd = root, # to reflect the real filesystem) users overriding this class # are responsible to set _cwd attribute as necessary. self._cwd = u('/') self._root = root self.cmd_channel = cmd_channel
def test_ftp2fs(self): # Tests for ftp2fs method. def join(x, y): return os.path.join(x, y.replace('/', os.sep)) ae = self.assertEqual fs = AbstractedFS(u('/'), None) def goforit(root): fs._root = root fs._cwd = u('/') ae(fs.ftp2fs(u('')), root) ae(fs.ftp2fs(u('/')), root) ae(fs.ftp2fs(u('.')), root) ae(fs.ftp2fs(u('..')), root) ae(fs.ftp2fs(u('a')), join(root, u('a'))) ae(fs.ftp2fs(u('/a')), join(root, u('a'))) ae(fs.ftp2fs(u('/a/')), join(root, u('a'))) ae(fs.ftp2fs(u('a/..')), root) ae(fs.ftp2fs(u('a/b')), join(root, u(r'a/b'))) ae(fs.ftp2fs(u('/a/b')), join(root, u(r'a/b'))) ae(fs.ftp2fs(u('/a/b/..')), join(root, u('a'))) ae(fs.ftp2fs(u('/a/b/../..')), root) fs._cwd = u('/sub') ae(fs.ftp2fs(u('')), join(root, u('sub'))) ae(fs.ftp2fs(u('/')), root) ae(fs.ftp2fs(u('.')), join(root, u('sub'))) ae(fs.ftp2fs(u('..')), root) ae(fs.ftp2fs(u('a')), join(root, u('sub/a'))) ae(fs.ftp2fs(u('a/')), join(root, u('sub/a'))) ae(fs.ftp2fs(u('a/..')), join(root, u('sub'))) ae(fs.ftp2fs(u('a/b')), join(root, 'sub/a/b')) ae(fs.ftp2fs(u('a/b/..')), join(root, u('sub/a'))) ae(fs.ftp2fs(u('a/b/../..')), join(root, u('sub'))) ae(fs.ftp2fs(u('a/b/../../..')), root) # UNC paths must be collapsed ae(fs.ftp2fs(u('//a')), join(root, u('a'))) if os.sep == '\\': goforit(u(r'C:\dir')) goforit(u('C:\\')) # on DOS-derived filesystems (e.g. Windows) this is the same # as specifying the current drive directory (e.g. 'C:\\') goforit(u('\\')) elif os.sep == '/': goforit(u('/home/user')) goforit(u('/')) else: # os.sep == ':'? Don't know... let's try it anyway goforit(getcwdu())
def test_fs2ftp(self): # Tests for fs2ftp method. def join(x, y): return os.path.join(x, y.replace('/', os.sep)) ae = self.assertEqual fs = AbstractedFS(u('/'), None) def goforit(root): fs._root = root ae(fs.fs2ftp(root), u('/')) ae(fs.fs2ftp(join(root, u('/'))), u('/')) ae(fs.fs2ftp(join(root, u('.'))), u('/')) # can't escape from root ae(fs.fs2ftp(join(root, u('..'))), u('/')) ae(fs.fs2ftp(join(root, u('a'))), u('/a')) ae(fs.fs2ftp(join(root, u('a/'))), u('/a')) ae(fs.fs2ftp(join(root, u('a/..'))), u('/')) ae(fs.fs2ftp(join(root, u('a/b'))), u('/a/b')) ae(fs.fs2ftp(join(root, u('a/b'))), u('/a/b')) ae(fs.fs2ftp(join(root, u('a/b/..'))), u('/a')) ae(fs.fs2ftp(join(root, u('/a/b/../..'))), u('/')) fs._cwd = u('/sub') ae(fs.fs2ftp(join(root, 'a/')), u('/a')) if os.sep == '\\': goforit(u(r'C:\dir')) goforit(u('C:\\')) # on DOS-derived filesystems (e.g. Windows) this is the same # as specifying the current drive directory (e.g. 'C:\\') goforit(u('\\')) fs._root = u(r'C:\dir') ae(fs.fs2ftp(u('C:\\')), u('/')) ae(fs.fs2ftp(u('D:\\')), u('/')) ae(fs.fs2ftp(u('D:\\dir')), u('/')) elif os.sep == '/': goforit(u('/')) if os.path.realpath('/__home/user') != '/__home/user': self.fail('Test skipped (symlinks not allowed).') goforit(u('/__home/user')) fs._root = u('/__home/user') ae(fs.fs2ftp(u('/__home')), u('/')) ae(fs.fs2ftp(u('/')), u('/')) ae(fs.fs2ftp(u('/__home/userx')), u('/')) else: # os.sep == ':'? Don't know... let's try it anyway goforit(getcwdu())
def goforit(root): fs._root = root ae(fs.fs2ftp(root), u('/')) ae(fs.fs2ftp(join(root, u('/'))), u('/')) ae(fs.fs2ftp(join(root, u('.'))), u('/')) # can't escape from root ae(fs.fs2ftp(join(root, u('..'))), u('/')) ae(fs.fs2ftp(join(root, u('a'))), u('/a')) ae(fs.fs2ftp(join(root, u('a/'))), u('/a')) ae(fs.fs2ftp(join(root, u('a/..'))), u('/')) ae(fs.fs2ftp(join(root, u('a/b'))), u('/a/b')) ae(fs.fs2ftp(join(root, u('a/b'))), u('/a/b')) ae(fs.fs2ftp(join(root, u('a/b/..'))), u('/a')) ae(fs.fs2ftp(join(root, u('/a/b/../..'))), u('/')) fs._cwd = u('/sub') ae(fs.fs2ftp(join(root, 'a/')), u('/a'))
def test_ftpnorm(self): # Tests for ftpnorm method. ae = self.assertEqual fs = AbstractedFS(u('/'), None) fs._cwd = u('/') ae(fs.ftpnorm(u('')), u('/')) ae(fs.ftpnorm(u('/')), u('/')) ae(fs.ftpnorm(u('.')), u('/')) ae(fs.ftpnorm(u('..')), u('/')) ae(fs.ftpnorm(u('a')), u('/a')) ae(fs.ftpnorm(u('/a')), u('/a')) ae(fs.ftpnorm(u('/a/')), u('/a')) ae(fs.ftpnorm(u('a/..')), u('/')) ae(fs.ftpnorm(u('a/b')), '/a/b') ae(fs.ftpnorm(u('a/b/..')), u('/a')) ae(fs.ftpnorm(u('a/b/../..')), u('/')) fs._cwd = u('/sub') ae(fs.ftpnorm(u('')), u('/sub')) ae(fs.ftpnorm(u('/')), u('/')) ae(fs.ftpnorm(u('.')), u('/sub')) ae(fs.ftpnorm(u('..')), u('/')) ae(fs.ftpnorm(u('a')), u('/sub/a')) ae(fs.ftpnorm(u('a/')), u('/sub/a')) ae(fs.ftpnorm(u('a/..')), u('/sub')) ae(fs.ftpnorm(u('a/b')), u('/sub/a/b')) ae(fs.ftpnorm(u('a/b/')), u('/sub/a/b')) ae(fs.ftpnorm(u('a/b/..')), u('/sub/a')) ae(fs.ftpnorm(u('a/b/../..')), u('/sub')) ae(fs.ftpnorm(u('a/b/../../..')), u('/')) ae(fs.ftpnorm(u('//')), u('/')) # UNC paths must be collapsed
def goforit(root): fs._root = root fs._cwd = u('/') ae(fs.ftp2fs(u('')), root) ae(fs.ftp2fs(u('/')), root) ae(fs.ftp2fs(u('.')), root) ae(fs.ftp2fs(u('..')), root) ae(fs.ftp2fs(u('a')), join(root, u('a'))) ae(fs.ftp2fs(u('/a')), join(root, u('a'))) ae(fs.ftp2fs(u('/a/')), join(root, u('a'))) ae(fs.ftp2fs(u('a/..')), root) ae(fs.ftp2fs(u('a/b')), join(root, u(r'a/b'))) ae(fs.ftp2fs(u('/a/b')), join(root, u(r'a/b'))) ae(fs.ftp2fs(u('/a/b/..')), join(root, u('a'))) ae(fs.ftp2fs(u('/a/b/../..')), root) fs._cwd = u('/sub') ae(fs.ftp2fs(u('')), join(root, u('sub'))) ae(fs.ftp2fs(u('/')), root) ae(fs.ftp2fs(u('.')), join(root, u('sub'))) ae(fs.ftp2fs(u('..')), root) ae(fs.ftp2fs(u('a')), join(root, u('sub/a'))) ae(fs.ftp2fs(u('a/')), join(root, u('sub/a'))) ae(fs.ftp2fs(u('a/..')), join(root, u('sub'))) ae(fs.ftp2fs(u('a/b')), join(root, 'sub/a/b')) ae(fs.ftp2fs(u('a/b/..')), join(root, u('sub/a'))) ae(fs.ftp2fs(u('a/b/../..')), join(root, u('sub'))) ae(fs.ftp2fs(u('a/b/../../..')), root) # UNC paths must be collapsed ae(fs.ftp2fs(u('//a')), join(root, u('a')))