Skip to content

Commit 6a5bf05

Browse files
committed
Fix the non-writable path deletion error
1 parent db46446 commit 6a5bf05

File tree

1 file changed

+31
-9
lines changed

1 file changed

+31
-9
lines changed

jupyter_server/services/contents/filemanager.py

+31-9
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ def is_hidden(self, path):
155155
os_path = self._get_os_path(path=path)
156156
return is_hidden(os_path, self.root_dir)
157157

158+
def is_writable(self, path):
159+
"""Does the API style path correspond to a writable directory or file?
160+
161+
Parameters
162+
----------
163+
path : string
164+
The path to check. This is an API path (`/` separated,
165+
relative to root_dir).
166+
167+
Returns
168+
-------
169+
hidden : bool
170+
Whether the path exists and is writable.
171+
"""
172+
path = path.strip("/")
173+
os_path = self._get_os_path(path=path)
174+
try:
175+
return os.access(os_path, os.W_OK)
176+
except OSError:
177+
self.log.error("Failed to check write permissions on %s", os_path)
178+
return False
179+
158180
def file_exists(self, path):
159181
"""Returns True if the file exists, else returns False.
160182
@@ -251,12 +273,8 @@ def _base_model(self, path):
251273
model["format"] = None
252274
model["mimetype"] = None
253275
model["size"] = size
276+
model["writable"] = self.is_writable(path)
254277

255-
try:
256-
model["writable"] = os.access(os_path, os.W_OK)
257-
except OSError:
258-
self.log.error("Failed to check write permissions on %s", os_path)
259-
model["writable"] = False
260278
return model
261279

262280
def _dir_model(self, path, content=True):
@@ -514,10 +532,12 @@ def is_non_empty_dir(os_path):
514532
# deleting non-empty files. See Github issue 3631.
515533
raise web.HTTPError(400, u"Directory %s not empty" % os_path)
516534
if _check_trash(os_path):
517-
self.log.debug("Sending %s to trash", os_path)
518535
# Looking at the code in send2trash, I don't think the errors it
519536
# raises let us distinguish permission errors from other errors in
520-
# code. So for now, just let them all get logged as server errors.
537+
# code. So for now, the "look before you leap" approach is used.
538+
if not self.is_writable(path):
539+
raise web.HTTPError(403, u"Permission denied: %s" % path)
540+
self.log.debug("Sending %s to trash", os_path)
521541
send2trash(os_path)
522542
return
523543
else:
@@ -842,10 +862,12 @@ async def is_non_empty_dir(os_path):
842862
# deleting non-empty files. See Github issue 3631.
843863
raise web.HTTPError(400, u"Directory %s not empty" % os_path)
844864
if await _check_trash(os_path):
845-
self.log.debug("Sending %s to trash", os_path)
846865
# Looking at the code in send2trash, I don't think the errors it
847866
# raises let us distinguish permission errors from other errors in
848-
# code. So for now, just let them all get logged as server errors.
867+
# code. So for now, the "look before you leap" approach is used.
868+
if not self.is_writable(path):
869+
raise web.HTTPError(403, u"Permission denied: %s" % path)
870+
self.log.debug("Sending %s to trash", os_path)
849871
send2trash(os_path)
850872
return
851873
else:

0 commit comments

Comments
 (0)