From af00fe715a59573761adc16f2e36a302bf10e3ed Mon Sep 17 00:00:00 2001 From: Alexandru Mihai <92.alexandru.mihai@gmail.com> Date: Sun, 23 Oct 2016 13:45:32 +0300 Subject: [PATCH 1/6] Resolved #1299 - Allow StaticRoute to follow symlinks --- aiohttp/web_urldispatcher.py | 11 +++++++---- docs/web.rst | 5 +++++ docs/web_reference.rst | 7 ++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 4882e21f654..4121892fcff 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -335,7 +335,7 @@ class StaticResource(PrefixResource): def __init__(self, prefix, directory, *, name=None, expect_handler=None, chunk_size=256*1024, response_factory=StreamResponse, - show_index=False): + show_index=False, follow_symlinks=False): super().__init__(prefix, name=name) try: directory = Path(directory) @@ -351,6 +351,7 @@ def __init__(self, prefix, directory, *, name=None, self._file_sender = FileSender(resp_factory=response_factory, chunk_size=chunk_size) self._show_index = show_index + self._follow_symlinks = follow_symlinks self._routes = {'GET': ResourceRoute('GET', self._handle, self, expect_handler=expect_handler), @@ -397,7 +398,8 @@ def _handle(self, request): filename = unquote(request.match_info['filename']) try: filepath = self._directory.joinpath(filename).resolve() - filepath.relative_to(self._directory) + if not self._follow_symlinks: + filepath.relative_to(self._directory) except (ValueError, FileNotFoundError) as error: # relatively safe raise HTTPNotFound() from error @@ -709,7 +711,7 @@ def add_route(self, method, path, handler, def add_static(self, prefix, path, *, name=None, expect_handler=None, chunk_size=256*1024, response_factory=StreamResponse, - show_index=False): + show_index=False, follow_symlinks=False): """Add static files view. prefix - url prefix @@ -725,7 +727,8 @@ def add_static(self, prefix, path, *, name=None, expect_handler=None, expect_handler=expect_handler, chunk_size=chunk_size, response_factory=response_factory, - show_index=show_index) + show_index=show_index, + follow_symlinks=follow_symlinks) self._reg_resource(resource) return resource diff --git a/docs/web.rst b/docs/web.rst index 2dd87940120..5c0d3029792 100644 --- a/docs/web.rst +++ b/docs/web.rst @@ -348,6 +348,11 @@ instead could be enabled with ``show_index`` parameter set to ``True``:: app.router.add_static('/prefix', path_to_static_folder, show_index=True) +When a symlink from the static directory is accessed, the server respondes to +client with ``HTTP/404 Not Found`` by default. To allow the server to follow +symlinks, parameter ``follow_symlinks`` should be set to ``True``:: + + app.router.add_static('/prefix', path_to_static_folder, follow_symlinks=True) Template Rendering ------------------ diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 1dbe5e3e66c..e34a7fa8a89 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -1464,7 +1464,8 @@ Router is any object that implements :class:`AbstractRouter` interface. .. method:: add_static(prefix, path, *, name=None, expect_handler=None, \ chunk_size=256*1024, \ response_factory=StreamResponse, \ - show_index=False) + show_index=False, \ + follow_symlinks=False) Adds a router and a handler for returning static files. @@ -1520,6 +1521,10 @@ Router is any object that implements :class:`AbstractRouter` interface. by default it's not allowed and HTTP/403 will be returned on directory access. + :param bool follow_symlinks: flag for allowing to follow symlinks from + a directory, by default it's not allowed and + HTTP/404 will be returned on access. + :returns: new :class:`StaticRoute` instance. .. coroutinemethod:: resolve(request) From e6d312302922bc15fa70aa996a2c1cc779d1dc2f Mon Sep 17 00:00:00 2001 From: Alexandru Mihai <92.alexandru.mihai@gmail.com> Date: Sun, 23 Oct 2016 13:49:26 +0300 Subject: [PATCH 2/6] Updated CHANGES with issue no --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 29848057c5e..ae1fb8e34a2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -47,7 +47,7 @@ CHANGES - Added save and load functionality for `CookieJar` #1219 -- +- Added option on `StaticRoute` to follow symlinks #1299 - From 12ed4e4cbcd58a1761530230bfc3ce68735b1a8b Mon Sep 17 00:00:00 2001 From: Alexandru Mihai <92.alexandru.mihai@gmail.com> Date: Sun, 23 Oct 2016 14:18:05 +0300 Subject: [PATCH 3/6] added symlinks to spelling --- docs/spelling_wordlist.txt | 1 + docs/web.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index b6efad00293..db7d7bee742 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -174,6 +174,7 @@ subprotocol subprotocols subtype Svetlov +symlinks syscall syscalls TCP diff --git a/docs/web.rst b/docs/web.rst index 5c0d3029792..f561fa7ba79 100644 --- a/docs/web.rst +++ b/docs/web.rst @@ -348,7 +348,7 @@ instead could be enabled with ``show_index`` parameter set to ``True``:: app.router.add_static('/prefix', path_to_static_folder, show_index=True) -When a symlink from the static directory is accessed, the server respondes to +When a symlink from the static directory is accessed, the server responses to client with ``HTTP/404 Not Found`` by default. To allow the server to follow symlinks, parameter ``follow_symlinks`` should be set to ``True``:: From e947bd6a217a5a78f00af9b356375e8fa247f632 Mon Sep 17 00:00:00 2001 From: Alexandru Mihai <92.alexandru.mihai@gmail.com> Date: Sun, 23 Oct 2016 14:25:05 +0300 Subject: [PATCH 4/6] also added symlink in spelling --- docs/spelling_wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index db7d7bee742..9f87bea8b12 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -174,6 +174,7 @@ subprotocol subprotocols subtype Svetlov +symlink symlinks syscall syscalls From 107f500a33f2ab107bb199c644f3919173549240 Mon Sep 17 00:00:00 2001 From: Alexandru Mihai <92.alexandru.mihai@gmail.com> Date: Sun, 23 Oct 2016 15:07:31 +0300 Subject: [PATCH 5/6] Added test for follow_symlinks --- tests/test_web_urldispatcher.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 2bd3e550048..2475c83dc6d 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -75,6 +75,38 @@ def test_access_root_of_static_handler(tmp_dir_path, loop, test_client, yield from r.release() +@pytest.mark.parametrize('data', ['hello world']) +@asyncio.coroutine +def test_follow_symlink(tmp_dir_path, loop, test_client, data): + """ + Tests the access to a looped symlink, which could not be resolved. + """ + print('aaaaa', tmp_dir_path) + + my_dir_path = os.path.join(tmp_dir_path, 'my_dir') + os.mkdir(my_dir_path) + + my_file_path = os.path.join(my_dir_path, 'my_file_in_dir') + with open(my_file_path, 'w') as fw: + fw.write(data) + + my_symlink_path = os.path.join(tmp_dir_path, 'my_symlink') + os.symlink(my_dir_path, my_symlink_path) + + app = web.Application(loop=loop) + + # Register global static route: + app.router.add_static('/', tmp_dir_path, follow_symlinks=True) + client = yield from test_client(app) + + # Request the root of the static directory. + r = yield from client.get('/my_symlink/my_file_in_dir') + assert r.status == 200 + assert (yield from r.text()) == data + + yield from r.release() + + @pytest.mark.parametrize('dir_name,filename,data', [ ('', 'test file.txt', 'test text'), ('test dir name', 'test dir file .txt', 'test text file folder') From f0ce5fa31d47f7a5815d79ea0f327d04ec47c475 Mon Sep 17 00:00:00 2001 From: Alexandru Mihai <92.alexandru.mihai@gmail.com> Date: Sun, 23 Oct 2016 15:09:09 +0300 Subject: [PATCH 6/6] Removed print and changed doc --- tests/test_web_urldispatcher.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 2475c83dc6d..e29f3875e5b 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -79,10 +79,8 @@ def test_access_root_of_static_handler(tmp_dir_path, loop, test_client, @asyncio.coroutine def test_follow_symlink(tmp_dir_path, loop, test_client, data): """ - Tests the access to a looped symlink, which could not be resolved. + Tests the access to a symlink, in static folder """ - print('aaaaa', tmp_dir_path) - my_dir_path = os.path.join(tmp_dir_path, 'my_dir') os.mkdir(my_dir_path)