pallets-eco / flask-admin

Simple and extensible administrative interface framework for Flask
https://flask-admin.readthedocs.io
BSD 3-Clause "New" or "Revised" License
5.8k stars 1.58k forks source link

HTTP 500 in FileAdmin if there are broken symlinks #2388

Open xmcp opened 1 year ago

xmcp commented 1 year ago

Step to reproduce:

Step 1. Create a broken symbolic link: image

Step 2. Serve this directory with FileAdmin:

from flask import Flask
from flask_admin import Admin
from flask_admin.contrib.fileadmin import FileAdmin

app = Flask(__name__)

admin = Admin(app, template_mode='bootstrap3')
admin.add_view(FileAdmin('static', '/static/', name='Static Files'))

app.run(host='127.0.0.1', port=5432)

Step 3. Visit http://127.0.0.1:5432/admin/fileadmin/

Actual Behavior:

It shows HTTP 500 Internal Server Error page with this exception in STDOUT:

[2023-10-06 21:57:06,091] ERROR in app: Exception on /admin/fileadmin/ [GET]
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 2070, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1515, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1513, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1499, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/home/xmcp/.local/lib/python3.8/site-packages/flask_admin/base.py", line 69, in inner
    return self._run_view(f, *args, **kwargs)
  File "/home/xmcp/.local/lib/python3.8/site-packages/flask_admin/base.py", line 369, in _run_view
    return fn(self, *args, **kwargs)
  File "/home/xmcp/.local/lib/python3.8/site-packages/flask_admin/contrib/fileadmin/__init__.py", line 833, in index_view
    for item in self.storage.get_files(path, directory):
  File "/home/xmcp/.local/lib/python3.8/site-packages/flask_admin/contrib/fileadmin/__init__.py", line 69, in get_files
    size = op.getsize(fp)
  File "/usr/lib/python3.8/genericpath.py", line 50, in getsize
    return os.stat(filename).st_size
FileNotFoundError: [Errno 2] No such file or directory: 'static/test'

Expected Behavior:

It should not show HTTP 500 page when fails to get the file size and mtime. Instead it should falls back with some value (e.g., 0).

Background:

I come across this issue when deleting a symlinked file in FileAdmin. Say that a directory has a file A that is symlinked to file B, and I delete file B, it will immediately show HTTP 500, preventing me to perform any action in this directory. Therefore I have to carefully delete A before B.

xmcp commented 1 year ago

To fix this issue, apart from patching LocalFileStorage.get_files, the method LocalFileStorage.path_exists also needs to return True for broken symlinks. I can open a PR for this if needed.

samuelhwilliams commented 4 months ago

I agree that we should handle this case better. We're currently preparing Flask-Admin as a pallets-eco project, but I think after that a PR would be helpful.